pymodbus-2.1.0/ 0000775 0000000 0000000 00000000000 13355134677 0013372 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/.coveragerc 0000664 0000000 0000000 00000000040 13355134677 0015505 0 ustar 00root root 0000000 0000000 [run]
omit =
pymodbus/repl/* pymodbus-2.1.0/.github/ 0000775 0000000 0000000 00000000000 13355134677 0014732 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/.github/ISSUE_TEMPLATE.md 0000664 0000000 0000000 00000001432 13355134677 0017437 0 ustar 00root root 0000000 0000000
### Versions
* Python:
* OS:
* Pymodbus:
* Modbus Hardware (if used):
### Pymodbus Specific
* Server: tcp/rtu/ascii - sync/async
* Client: tcp/rtu/ascii - sync/async
### Description
What were you trying, what has happened, what went wrong, and what did you expect?
### Code and Logs
```python
# code and logs here.
```
pymodbus-2.1.0/.github/PULL_REQUEST_TEMPLATE.md 0000664 0000000 0000000 00000000116 13355134677 0020531 0 ustar 00root root 0000000 0000000
pymodbus-2.1.0/.gitignore 0000664 0000000 0000000 00000001036 13355134677 0015362 0 ustar 00root root 0000000 0000000 *.pyc
*.swp
build/
dist/
pymodbus.egg-info/
.coverage
.vscode
.idea
.noseids
*.db
.idea/
.tox/
doc/api/epydoc/html/
.vscode/
.venv
__pycache__/
pymodbus/__pycache__/
pymodbus/client/__pycache__/
pymodbus/datastore/__pycache__/
pymodbus/internal/__pycache__/
pymodbus/server/__pycache__/
test/__pycache__/
**/pymodbus.db
/.eggs/
/test/bin/
/test/include/
/test/lib/
/test/pip-selfcheck.json
/test/.Python
/.cache/
/doc/sphinx/doctrees/
/doc_new/
/doc/quality/
/doc/pymodbus.pdf
/doc/sphinx/
/doc/html/
/doc/_build/
.pytest_cache/
**/.pymodhis
pymodbus-2.1.0/.project 0000664 0000000 0000000 00000000552 13355134677 0015043 0 ustar 00root root 0000000 0000000
pymodbusorg.python.pydev.PyDevBuilderorg.python.pydev.pythonNature
pymodbus-2.1.0/.pydevproject 0000664 0000000 0000000 00000000651 13355134677 0016113 0 ustar 00root root 0000000 0000000
/pymodbuspython 2.7Pymodbus Environment
pymodbus-2.1.0/.readthedocs.yml 0000664 0000000 0000000 00000000303 13355134677 0016454 0 ustar 00root root 0000000 0000000 # Build PDF & ePub
formats:
- epub
- pdf
requirements_file: requirements-docs.txt
python:
extra_requirements:
- twisted
- tornado
- documents
version: 3.5
pymodbus-2.1.0/.travis.yml 0000664 0000000 0000000 00000001534 13355134677 0015506 0 ustar 00root root 0000000 0000000 sudo: false
language: python
matrix:
include:
- os: linux
python: "2.7"
- os: linux
python: "3.4"
- os: linux
python: "3.5"
- os: linux
python: "3.6"
- os: osx
osx_image: xcode8.3
language: generic
before_install:
- if [ $TRAVIS_OS_NAME = osx ]; then brew update; fi
- if [ $TRAVIS_OS_NAME = osx ]; then brew install openssl; fi
install:
# - scripts/travis.sh pip install pip-accel
- scripts/travis.sh pip install -U setuptools
- scripts/travis.sh pip install coveralls
- scripts/travis.sh pip install --requirement=requirements-checks.txt
- scripts/travis.sh pip install --requirement=requirements-tests.txt
- scripts/travis.sh LC_ALL=C pip install .
script:
# - scripts/travis.sh make check
- scripts/travis.sh make test
after_success:
- scripts/travis.sh coveralls
branches:
except:
- /^[0-9]/
pymodbus-2.1.0/CHANGELOG.rst 0000664 0000000 0000000 00000017770 13355134677 0015427 0 ustar 00root root 0000000 0000000 Version 2.1.0
-----------------------------------------------------------
* Fix Issues with Serial client where in partial data was read when the response size is unknown.
* Fix Infinite sleep loop in RTU Framer.
* Add pygments as extra requirement for repl.
* Add support to modify modbus client attributes via repl.
* Update modbus repl documentation.
* More verbose logs for repl.
Version 2.0.1
-----------------------------------------------------------
* Fix unicode decoder error with BinaryPayloadDecoder in some platforms
* Avoid unnecessary import of deprecated modules with dependencies on twisted
Version 2.0.0
-----------------------------------------------------------
**Note This is a Major release and might affect your existing Async client implementation. Refer examples on how to use the latest async clients.**
* Async client implementation based on Tornado, Twisted and asyncio with backward compatibility support for twisted client.
* Allow reusing existing[running] asyncio loop when creating async client based on asyncio.
* Allow reusing address for Modbus TCP sync server.
* Add support to install tornado as extra requirement while installing pymodbus.
* Support Pymodbus REPL
* Add support to python 3.7.
* Bug fix and enhancements in examples.
Version 2.0.0rc1
-----------------------------------------------------------
**Note This is a Major release and might affect your existing Async client implementation. Refer examples on how to use the latest async clients.**
* Async client implementation based on Tornado, Twisted and asyncio
Version 1.5.2
------------------------------------------------------------
* Fix serial client `is_socket_open` method
Version 1.5.1
------------------------------------------------------------
* Fix device information selectors
* Fixed behaviour of the MEI device information command as a server when an invalid object_id is provided by an external client.
* Add support for repeated MEI device information Object IDs (client/server)
* Added support for encoding device information when it requires more than one PDU to pack.
* Added REPR statements for all syncchronous clients
* Added `isError` method to exceptions, Any response received can be tested for success before proceeding.
.. code-block:: python
res = client.read_holding_registers(...)
if not res.isError():
# proceed
else:
# handle error or raise
* Add examples for MEI read device information request
Version 1.5.0
------------------------------------------------------------
* Improve transaction speeds for sync clients (RTU/ASCII), now retry on empty happens only when retry_on_empty kwarg is passed to client during intialization
`client = Client(..., retry_on_empty=True)`
* Fix tcp servers (sync/async) not processing requests with transaction id > 255
* Introduce new api to check if the received response is an error or not (response.isError())
* Move timing logic to framers so that irrespective of client, correct timing logics are followed.
* Move framers from transaction.py to respective modules
* Fix modbus payload builder and decoder
* Async servers can now have an option to defer `reactor.run()` when using `StartServer(...,defer_reactor_run=True)`
* Fix UDP client issue while handling MEI messages (ReadDeviceInformationRequest)
* Add expected response lengths for WriteMultipleCoilRequest and WriteMultipleRegisterRequest
* Fix _rtu_byte_count_pos for GetCommEventLogResponse
* Add support for repeated MEI device information Object IDs
* Fix struct errors while decoding stray response
* Modbus read retries works only when empty/no message is received
* Change test runner from nosetest to pytest
* Fix Misc examples
Version 1.4.0
------------------------------------------------------------
* Bug fix Modbus TCP client reading incomplete data
* Check for slave unit id before processing the request for serial clients
* Bug fix serial servers with Modbus Binary Framer
* Bug fix header size for ModbusBinaryFramer
* Bug fix payload decoder with endian Little
* Payload builder and decoder can now deal with the wordorder as well of 32/64 bit data.
* Support Database slave contexts (SqlStore and RedisStore)
* Custom handlers could be passed to Modbus TCP servers
* Asynchronous Server could now be stopped when running on a seperate thread (StopServer)
* Signal handlers on Asyncronous servers are now handled based on current thread
* Registers in Database datastore could now be read from remote clients
* Fix examples in contrib (message_parser.py/message_generator.py/remote_server_context)
* Add new example for SqlStore and RedisStore (db store slave context)
* Fix minor comaptibility issues with utilities.
* Update test requirements
* Update/Add new unit tests
* Move twisted requirements to extra so that it is not installed by default on pymodbus installtion
Version 1.3.2
------------------------------------------------------------
* ModbusSerialServer could now be stopped when running on a seperate thread.
* Fix issue with server and client where in the frame buffer had values from previous unsuccesful transaction
* Fix response length calculation for ModbusASCII protocol
* Fix response length calculation ReportSlaveIdResponse, DiagnosticStatusResponse
* Fix never ending transaction case when response is recieved without header and CRC
* Fix tests
Version 1.3.1
------------------------------------------------------------
* Recall socket recv until get a complete response
* Register_write_message.py: Observe skip_encode option when encoding a single register request
* Fix wrong expected response length for coils and discrete inputs
* Fix decode errors with ReadDeviceInformationRequest and ReportSlaveIdRequest on Python3
* Move MaskWriteRegisterRequest/MaskWriteRegisterResponse to register_write_message.py from file_message.py
* Python3 compatible examples [WIP]
* Misc updates with examples
Version 1.3.0.rc2
------------------------------------------------------------
* Fix encoding problem for ReadDeviceInformationRequest method on python3
* Fix problem with the usage of ord in python3 while cleaning up receive buffer
* Fix struct unpack errors with BinaryPayloadDecoder on python3 - string vs bytestring error
* Calculate expected response size for ReadWriteMultipleRegistersRequest
* Enhancement for ModbusTcpClient, ModbusTcpClient can now accept connection timeout as one of the parameter
* Misc updates
Version 1.3.0.rc1
------------------------------------------------------------
* Timing improvements over MODBUS Serial interface
* Modbus RTU use 3.5 char silence before and after transactions
* Bug fix on FifoTransactionManager , flush stray data before transaction
* Update repository information
* Added ability to ignore missing slaves
* Added ability to revert to ZeroMode
* Passed a number of extra options through the stack
* Fixed documenation and added a number of examples
Version 1.2.0
------------------------------------------------------------
* Reworking the transaction managers to be more explicit and
to handle modbus RTU over TCP.
* Adding examples for a number of unique requested use cases
* Allow RTU framers to fail fast instead of staying at fault
* Working on datastore saving and loading
Version 1.1.0
------------------------------------------------------------
* Fixing memory leak in clients and servers (removed __del__)
* Adding the ability to override the client framers
* Working on web page api and GUI
* Moving examples and extra code to contrib sections
* Adding more documentation
Version 1.0.0
------------------------------------------------------------
* Adding support for payload builders to form complex encoding
and decoding of messages.
* Adding BCD and binary payload builders
* Adding support for pydev
* Cleaning up the build tools
* Adding a message encoding generator for testing.
* Now passing kwargs to base of PDU so arguments can be used
correctly at all levels of the protocol.
* A number of bug fixes (see bug tracker and commit messages)
Version 0.9.0
------------------------------------------------------------
Please view the git commit log
pymodbus-2.1.0/MANIFEST.in 0000664 0000000 0000000 00000000101 13355134677 0015120 0 ustar 00root root 0000000 0000000 include requirements.txt
include README.rst
include CHANGELOG.rst pymodbus-2.1.0/Makefile 0000664 0000000 0000000 00000004052 13355134677 0015033 0 ustar 00root root 0000000 0000000 # Makefile for the `pymodbus' package.
WORKON_HOME ?= $(HOME)/.virtualenvs
VIRTUAL_ENV ?= $(WORKON_HOME)/pymodbus
PATH := $(VIRTUAL_ENV)/bin:$(PATH)
MAKE := $(MAKE) --no-print-directory
SHELL = bash
default:
@echo 'Makefile for pymodbus'
@echo
@echo 'Usage:'
@echo
@echo ' make install install the package in a virtual environment'
@echo ' make reset recreate the virtual environment'
@echo ' make check check coding style (PEP-8, PEP-257)'
@echo ' make test run the test suite, report coverage'
@echo ' make tox run the tests on all Python versions'
@echo ' make docs creates sphinx documentation in html'
@echo ' make clean cleanup all temporary files'
@echo
install:
@test -d "$(VIRTUAL_ENV)" || mkdir -p "$(VIRTUAL_ENV)"
@test -x "$(VIRTUAL_ENV)/bin/python" || virtualenv --quiet "$(VIRTUAL_ENV)"
@test -x "$(VIRTUAL_ENV)/bin/pip" || easy_install pip
@pip install --quiet --requirement=requirements.txt
@pip uninstall --yes pymodbus &>/dev/null || true
@pip install --quiet --no-deps --ignore-installed .
reset:
$(MAKE) clean
rm -Rf "$(VIRTUAL_ENV)"
$(MAKE) install
check: install
@pip install --upgrade --quiet --requirement=requirements-checks.txt
@flake8
test: install
@pip install --quiet --requirement=requirements-tests.txt
@pytest --cov=pymodbus/ --cov-report term-missing
@coverage report --fail-under=90
tox: install
@pip install --quiet tox && tox
docs: install
@pip install --quiet --requirement=requirements-docs.txt
@cd doc && make clean && make html
publish: install
git push origin && git push --tags origin
$(MAKE) clean
pip install --quiet twine wheel
python setup.py sdist bdist_wheel
twine upload dist/*
$(MAKE) clean
clean:
@rm -Rf *.egg .eggs *.egg-info *.db .cache .coverage .tox build dist docs/build htmlcov doc/_build test/.Python test/pip-selfcheck.json test/lib/ test/include/ test/bin/
@find . -depth -type d -name __pycache__ -exec rm -Rf {} \;
@find . -type f -name '*.pyc' -delete
.PHONY: default install reset check test tox docs publish clean
pymodbus-2.1.0/README.rst 0000664 0000000 0000000 00000017436 13355134677 0015074 0 ustar 00root root 0000000 0000000 ================================
PyModbus - A Python Modbus Stack
================================
.. image:: https://travis-ci.org/riptideio/pymodbus.svg?branch=master
:target: https://travis-ci.org/riptideio/pymodbus
.. image:: https://badges.gitter.im/Join%20Chat.svg
:target: https://gitter.im/pymodbus_dev/Lobby
.. image:: https://readthedocs.org/projects/pymodbus/badge/?version=latest
:target: http://pymodbus.readthedocs.io/en/async/?badge=latest
:alt: Documentation Status
.. image:: http://pepy.tech/badge/pymodbus
:target: http://pepy.tech/project/pymodbus
:alt: Downloads
.. important::
**Note This is a Major release and might affect your existing Async client implementation. Refer examples on how to use the latest async clients.**
------------------------------------------------------------
Summary
------------------------------------------------------------
Pymodbus is a full Modbus protocol implementation using twisted for its
asynchronous communications core. It can also be used without any third
party dependencies (aside from pyserial) if a more lightweight project is
needed. Furthermore, it should work fine under any python version > 2.7
(including python 3+)
------------------------------------------------------------
Features
------------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~
Client Features
~~~~~~~~~~~~~~~~~~~~
* Full read/write protocol on discrete and register
* Most of the extended protocol (diagnostic/file/pipe/setting/information)
* TCP, UDP, Serial ASCII, Serial RTU, and Serial Binary
* asynchronous(powered by twisted/tornado/asyncio) and synchronous versions
* Payload builder/decoder utilities
* Pymodbus REPL for quick tests
~~~~~~~~~~~~~~~~~~~~
Server Features
~~~~~~~~~~~~~~~~~~~~
* Can function as a fully implemented modbus server
* TCP, UDP, Serial ASCII, Serial RTU, and Serial Binary
* asynchronous(powered by twisted) and synchronous versions
* Full server control context (device information, counters, etc)
* A number of backing contexts (database, redis, sqlite, a slave device)
------------------------------------------------------------
Use Cases
------------------------------------------------------------
Although most system administrators will find little need for a Modbus
server on any modern hardware, they may find the need to query devices on
their network for status (PDU, PDR, UPS, etc). Since the library is written
in python, it allows for easy scripting and/or integration into their existing
solutions.
Continuing, most monitoring software needs to be stress tested against
hundreds or even thousands of devices (why this was originally written), but
getting access to that many is unwieldy at best. The pymodbus server will allow
a user to test as many devices as their base operating system will allow (*allow*
in this case means how many Virtual IP addresses are allowed).
For more information please browse the project documentation:
http://riptideio.github.io/pymodbus/
or
http://readthedocs.org/docs/pymodbus/en/latest/index.html
------------------------------------------------------------
Example Code
------------------------------------------------------------
For those of you that just want to get started fast, here you go::
from pymodbus.client.sync import ModbusTcpClient
client = ModbusTcpClient('127.0.0.1')
client.write_coil(1, True)
result = client.read_coils(1,1)
print(result.bits[0])
client.close()
For more advanced examples, check out the examples included in the
respository. If you have created any utilities that meet a specific
need, feel free to submit them so others can benefit.
Also, if you have questions, please ask them on the mailing list
so that others can benefit from the results and so that I can
trace them. I get a lot of email and sometimes these requests
get lost in the noise: http://groups.google.com/group/pymodbus or
at gitter: https://gitter.im/pymodbus_dev/Lobby
------------------------------------------------------------
Pymodbus REPL (Read Evaluate Procee Loop)
------------------------------------------------------------
Starting with Pymodbus 2.x, pymodbus library comes with handy
Pymodbus REPL to quickly run the modbus clients in tcp/rtu modes.
Pymodbus REPL comes with many handy features such as payload decoder
to directly retrieve the values in desired format and supports all
the diagnostic function codes directly .
For more info on REPL refer `Pymodbus REPL `_
.. image:: https://asciinema.org/a/y1xOk7lm59U1bRBE2N1pDIj2o.png
:target: https://asciinema.org/a/y1xOk7lm59U1bRBE2N1pDIj2o
------------------------------------------------------------
Installing
------------------------------------------------------------
You can install using pip or easy install by issuing the following
commands in a terminal window (make sure you have correct
permissions or a virtualenv currently running)::
easy_install -U pymodbus
pip install -U pymodbus
To Install pymodbus with twisted support run::
pip install -U pymodbus[twisted]
To Install pymodbus with tornado support run::
pip install -U pymodbus[tornado]
To Install pymodbus REPL::
pip install -U pymodbus[repl]
Otherwise you can pull the trunk source and install from there::
git clone git://github.com/bashwork/pymodbus.git
cd pymodbus
python setup.py install
Either method will install all the required dependencies
(at their appropriate versions) for your current python distribution.
If you would like to install pymodbus without the twisted dependency,
simply edit the setup.py file before running easy_install and comment
out all mentions of twisted. It should be noted that without twisted,
one will only be able to run the synchronized version as the
asynchronous versions uses twisted for its event loop.
------------------------------------------------------------
Current Work In Progress
------------------------------------------------------------
Since I don't have access to any live modbus devices anymore
it is a bit hard to test on live hardware. However, if you would
like your device tested, I accept devices via mail or by IP address.
That said, the current work mainly involves polishing the library as
I get time doing such tasks as:
* Make PEP-8 compatible and flake8 ready
* Fixing bugs/feature requests
* Architecture documentation
* Functional testing against any reference I can find
* The remaining edges of the protocol (that I think no one uses)
* Asynchronous clients with support to tornado , asyncio
------------------------------------------------------------
Development Instructions
------------------------------------------------------------
The current code base is compatible with both py2 and py3.
Use make to perform a range of activities
::
$ make
Makefile for pymodbus
Usage:
make install install the package in a virtual environment
make reset recreate the virtual environment
make check check coding style (PEP-8, PEP-257)
make test run the test suite, report coverage
make tox run the tests on all Python versions
make clean cleanup all temporary files
------------------------------------------------------------
Contributing
------------------------------------------------------------
Just fork the repo and raise your PR against `dev` branch.
------------------------------------------------------------
License Information
------------------------------------------------------------
Pymodbus is built on top of code developed from/by:
* Copyright (c) 2001-2005 S.W.A.C. GmbH, Germany.
* Copyright (c) 2001-2005 S.W.A.C. Bohemia s.r.o., Czech Republic.
* Hynek Petrak, https://github.com/HynekPetrak
* Twisted Matrix
Released under the BSD License
pymodbus-2.1.0/doc/ 0000775 0000000 0000000 00000000000 13355134677 0014137 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/INSTALL 0000664 0000000 0000000 00000003122 13355134677 0015166 0 ustar 00root root 0000000 0000000 Requirements
-------------
* Python 2.3 or later.
* Python Twisted
* Pyserial
On Windows pywin32 is recommended (this is built in to ActivePython,
so no need to reinstall if you use it instead of standard Python):
http://sourceforge.net/project/showfiles.php?group_id=78018
The Windows IOCP reactor requires pywin32 build 205 or later.
Installation
-------------
To install the package from pypi, use either easy_install or pip::
pip install -U pymodbus
easy_install -U pymodbus
As with other Python packages, the standard way of installing from source
is (as root or administrator)::
python setup.py install
Running Tests
--------------
The tests can be run with the built in unittest module, however, it is
much easier to run with the nose package. With that installed, you can
use either of the following::
python setup.py test
nosetests
Building Documentation
----------------------
The documentation is written in restructured text using the sphinx module.
Building it is as simple as::
python setup build_sphinx
The API documents can be generated using one of four programs:
* epydoc
* pydoc
* pydoctor
* doxygen
To bulid these, simply run the following command and the available
packages will sipmly be built::
python setup.py build_apidocs
Quality Tests
----------------------
There are a number of quality tests that can be run against the code base
aside from unit tests::
python setup.py scan_2to3 # run a python3 compatability test
python setup.py pep8 # run a pop8 standards test
python setup.py lint # run a lint test
pymodbus-2.1.0/doc/LICENSE 0000664 0000000 0000000 00000002561 13355134677 0015150 0 ustar 00root root 0000000 0000000 Copyright (c) 2011 Galen Collins
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
pymodbus-2.1.0/doc/Makefile 0000664 0000000 0000000 00000001135 13355134677 0015577 0 ustar 00root root 0000000 0000000 # Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = PyModbus
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) pymodbus-2.1.0/doc/TODO 0000664 0000000 0000000 00000003520 13355134677 0014627 0 ustar 00root root 0000000 0000000 ---------------------------------------------------------------------------
General
---------------------------------------------------------------------------
- reorganize code into folder namespaces
- put protocol code in protocol namespace
- make framer read header->read header.length
- maybe just for sync
- finish clients (and interface)
- add all modbus control into server
- add a frontend plugin system
- web frontend (bottle)
- twisted trial / twisted logging (for functional async tests)
- twisted serial server
- add daemonize code / init.d / config (or just use twisted)
- add correct transaction handling (retry, fail, etc)
- finish remaining functions
---------------------------------------------------------------------------
Protocols
---------------------------------------------------------------------------
- Serial RTU -> just use sleep wait
- Test serial against devices (and virtual tty)
---------------------------------------------------------------------------
Utilities
---------------------------------------------------------------------------
- (tcp/serial) forwarder
- (udp/serial) forwarder
---------------------------------------------------------------------------
Client
---------------------------------------------------------------------------
- Rework transaction flow and response data
---------------------------------------------------------------------------
Tools
---------------------------------------------------------------------------
- add functional tests
- add tk and wx gui frontdends (with editable data tables)
- rpm and deb packages (documentation)
---------------------------------------------------------------------------
Scratch
---------------------------------------------------------------------------
from twisted.python import log
observer = log.PythonLoggingObserver()
observer.start()
pymodbus-2.1.0/doc/api/ 0000775 0000000 0000000 00000000000 13355134677 0014710 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/api/doxygen/ 0000775 0000000 0000000 00000000000 13355134677 0016365 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/api/doxygen/.doxygen 0000664 0000000 0000000 00000173714 13355134677 0020060 0 ustar 00root root 0000000 0000000 # Doxyfile 1.5.8
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the config file
# that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# http://www.gnu.org/software/libiconv for the list of possible encodings.
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = Pymodbus
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = 0.5
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY =
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
# 4096 sub-directories (in 2 levels) under the output directory of each output
# format and will distribute the generated files over these directories.
# Enabling this option can be useful when feeding doxygen a huge amount of
# source files, where putting all generated files in the same directory would
# otherwise cause performance problems for the file system.
CREATE_SUBDIRS = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
# Spanish, Swedish, and Ukrainian.
OUTPUT_LANGUAGE = English
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator
# that is used to form the text in various listings. Each string
# in this list, if found as the leading text of the brief description, will be
# stripped from the text and the result after processing the whole list, is
# used as the annotated text. Otherwise, the brief description is used as-is.
# If left blank, the following values are used ("$name" is automatically
# replaced with the name of the entity): "The $name class" "The $name widget"
# "The $name file" "is" "provides" "specifies" "contains"
# "represents" "a" "an" "the"
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = YES
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user-defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the
# path to strip.
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
# the path mentioned in the documentation of a class, which tells
# the reader which header file to include in order to use a class.
# If left blank only the name of the header file containing the class
# definition is used. Otherwise one should specify the include paths that
# are normally passed to the compiler using the -I flag.
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
# comments will behave just like regular Qt-style comments
# (thus requiring an explicit @brief command for a brief description.)
JAVADOC_AUTOBRIEF = NO
# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
# interpret the first line (until the first dot) of a Qt-style
# comment as the brief description. If set to NO, the comments
# will behave just like regular Qt-style comments (thus requiring
# an explicit \brief command for a brief description.)
QT_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
# treat a multi-line C++ special comment block (i.e. a block of //! or ///
# comments) as a brief description. This used to be the default behaviour.
# The new default is to treat a multi-line C++ comment block as a detailed
# description. Set this tag to YES if you prefer the old behaviour instead.
MULTILINE_CPP_IS_BRIEF = NO
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# re-implements.
INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
# a new page for each member. If set to NO, the documentation of a member will
# be part of the file/class/namespace that contains it.
SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 4
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
# sources only. Doxygen will then generate output that is more tailored for C.
# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
# sources only. Doxygen will then generate output that is more tailored for
# Java. For instance, namespaces will be presented as packages, qualified
# scopes will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
# sources only. Doxygen will then generate output that is more tailored for
# Fortran.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
# sources. Doxygen will then generate output that is tailored for
# VHDL.
OPTIMIZE_OUTPUT_VHDL = NO
# Doxygen selects the parser to use depending on the extension of the files it parses.
# With this tag you can assign which parser to use for a given extension.
# Doxygen has a built-in mapping, but you can override or extend it using this tag.
# The format is ext=language, where ext is a file extension, and language is one of
# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C
EXTENSION_MAPPING =
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should
# set this tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
# func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
# Doxygen will parse them like normal C++ but will assume all classes use public
# instead of private inheritance when no explicit protection keyword is present.
SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate getter
# and setter methods for a property. Setting this option to YES (the default)
# will make doxygen to replace the get and set methods by a property in the
# documentation. This will only work if the methods are indeed getting or
# setting a simple type. If this is not the case, or you want to show the
# methods anyway, you should set this option to NO.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a
# subgroup of that type (e.g. under the Public Functions section). Set it to
# NO to prevent subgrouping. Alternatively, this can be done per class using
# the \nosubgrouping command.
SUBGROUPING = YES
# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
# is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
# namespace, or class. And the struct will be named TypeS. This can typically
# be useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
TYPEDEF_HIDES_STRUCT = NO
# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
# determine which symbols to keep in memory and which to flush to disk.
# When the cache is full, less often used symbols will be written to disk.
# For small to medium size projects (<1000 input files) the default value is
# probably good enough. For larger projects a too small cache size can cause
# doxygen to be busy swapping symbols to and from disk most of the time
# causing a significant performance penality.
# If the system has enough physical memory increasing the cache will improve the
# performance by keeping more symbols in memory. Note that the value works on
# a logarithmic scale so increasing the size by one will rougly double the
# memory usage. The cache size is given by this formula:
# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
# corresponding to a cache size of 2^16 = 65536 symbols
SYMBOL_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
# This flag is only useful for Objective-C code. When set to YES local
# methods, which are defined in the implementation section but not in
# the interface are included in the documentation.
# If set to NO (the default) only methods in the interface are included.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
# 'anonymous_namespace{file}', where file will be replaced with the base
# name of the file that contains the anonymous namespace. By default
# anonymous namespace are hidden.
EXTRACT_ANON_NSPACES = NO
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these classes will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
# friend (class|struct|union) declarations.
# If set to NO (the default) these declarations will be included in the
# documentation.
HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
# documentation blocks found inside the body of a function.
# If set to NO (the default) these blocks will be appended to the
# function's detailed documentation block.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower-case letters. If set to YES upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put a list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = YES
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
# brief documentation of file, namespace and class members alphabetically
# by member name. If set to NO (the default) the members will appear in
# declaration order.
SORT_BRIEF_DOCS = NO
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
# hierarchy of group names into alphabetical order. If set to NO (the default)
# the group names will appear in their defined order.
SORT_GROUP_NAMES = NO
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
# sorted by fully-qualified names, including namespaces. If set to
# NO (the default), the class list will be sorted only by class name,
# not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the
# alphabetical list.
SORT_BY_SCOPE_NAME = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or
# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
# disable (NO) the deprecated list. This list is created by putting
# \deprecated commands in the documentation.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
# the initial value of a variable or define consists of for it to appear in
# the documentation. If the initializer consists of more lines than specified
# here it will be hidden. Use a value of 0 to hide initializers completely.
# The appearance of the initializer of individual variables and defines in the
# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
# If the sources in your project are distributed over multiple directories
# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
# in the documentation. The default is NO.
SHOW_DIRECTORIES = NO
# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
# This will remove the Files entry from the Quick Index and from the
# Folder Tree View (if specified). The default is YES.
SHOW_FILES = YES
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
# Namespaces page.
# This will remove the Namespaces entry from the Quick Index
# and from the Folder Tree View (if specified). The default is YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
# popen()) the command , where is the value of
# the FILE_VERSION_FILTER tag, and is the name of an input file
# provided by doxygen. Whatever the program writes to standard output
# is used as the file version. See the manual for examples.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
# doxygen. The layout file controls the global structure of the generated output files
# in an output format independent way. The create the layout file that represents
# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
# file name after the option, if omitted DoxygenLayout.xml will be used as the name
# of the layout file.
LAYOUT_FILE =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = YES
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = NO
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = NO
# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some
# parameters in a documented function, or documenting parameters that
# don't exist or using markup commands wrongly.
WARN_IF_DOC_ERROR = NO
# This WARN_NO_PARAMDOC option can be abled to get warnings for
# functions that are documented, but have no documentation for their parameters
# or return value. If set to NO (the default) doxygen will only warn about
# wrong or incomplete parameter documentation, but not about the absence of
# documentation.
WARN_NO_PARAMDOC = NO
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text. Optionally the format may contain
# $version, which will be replaced by the version of the file (if it could
# be obtained via FILE_VERSION_FILTER)
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning
# and error messages should be written. If left blank the output is written
# to stderr.
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ../../../pymodbus
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
# also the default input encoding. Doxygen uses libiconv (or the iconv built
# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
# the list of possible encodings.
INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
FILE_PATTERNS = *.py
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
# directories that are symbolic links (a Unix filesystem feature) are excluded
# from the input.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories. Note that the wildcards are matched
# against the file with absolute path, so to exclude all test directories
# for example use the pattern */test/*
EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command , where
# is the value of the INPUT_FILTER tag, and is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output.
# If FILTER_PATTERNS is specified, this tag will be
# ignored.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis.
# Doxygen will compare the file name with each pattern and apply the
# filter if there is a match.
# The filters are a list of the form:
# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
# is applied to all files.
FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
# Note: To get rid of all source code in the generated output, make sure also
# VERBATIM_HEADERS is set to NO.
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
# link to the source code.
# Otherwise they will link to the documentation.
REFERENCES_LINK_SOURCE = YES
# If the USE_HTAGS tag is set to YES then the references to source code
# will point to the HTML generated by the htags(1) tool instead of doxygen
# built-in source browser. The htags tool is part of GNU's global source
# tagging system (see http://www.gnu.org/software/global/global.html). You
# will need version 4.8.6 or higher.
USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = NO
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet. Note that doxygen will try to copy
# the style sheet file to the HTML output directory, so don't put your own
# stylesheet in the HTML output directory as well, or it will be erased!
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded. For this to work a browser that supports
# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
HTML_DYNAMIC_SECTIONS = NO
# If the GENERATE_DOCSET tag is set to YES, additional index files
# will be generated that can be used as input for Apple's Xcode 3
# integrated development environment, introduced with OSX 10.5 (Leopard).
# To create a documentation set, doxygen will generate a Makefile in the
# HTML output directory. Running make will produce the docset in that
# directory and running "make install" will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
# it at startup.
# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
GENERATE_DOCSET = NO
# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
# feed. A documentation feed provides an umbrella under which multiple
# documentation sets from a single provider (such as a company or product suite)
# can be grouped.
DOCSET_FEEDNAME = "Doxygen generated docs"
# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
# should uniquely identify the documentation set bundle. This should be a
# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
# will append .docset to the name.
DOCSET_BUNDLE_ID = org.doxygen.Project
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
# be used to specify the file name of the resulting .chm file. You
# can add a path in front of the file if the result should not be
# written to the html output directory.
CHM_FILE =
# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
# be used to specify the location (absolute path including file name) of
# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
# the HTML help compiler on the generated index.hhp.
HHC_LOCATION =
# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
# is used to encode HtmlHelp index (hhk), content (hhc) and project file
# content.
CHM_INDEX_ENCODING =
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the HTML help documentation and to the tree view.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
# are set, an additional index file will be generated that can be used as input for
# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
# HTML documentation.
GENERATE_QHP = NO
# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
# be used to specify the file name of the resulting .qch file.
# The path specified is relative to the HTML output folder.
QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating
# Qt Help Project output. For more information please see
# http://doc.trolltech.com/qthelpproject.html#namespace
QHP_NAMESPACE =
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
# Qt Help Project output. For more information please see
# http://doc.trolltech.com/qthelpproject.html#virtual-folders
QHP_VIRTUAL_FOLDER = doc
# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
# For more information please see
# http://doc.trolltech.com/qthelpproject.html#custom-filters
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
# Qt Help Project / Custom Filters.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
# filter section matches.
# Qt Help Project / Filter Attributes.
QHP_SECT_FILTER_ATTRS =
# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
# be used to specify the location of Qt's qhelpgenerator.
# If non-empty doxygen will try to run qhelpgenerator on the generated
# .qhp file.
QHG_LOCATION =
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.
ENUM_VALUES_PER_LINE = 4
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information.
# If the tag value is set to FRAME, a side panel will be generated
# containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
# probably better off using the HTML help feature. Other possible values
# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
# and Class Hierarchy pages using a tree view instead of an ordered list;
# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
# disables this behavior completely. For backwards compatibility with previous
# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
# respectively.
GENERATE_TREEVIEW = NO
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
# Use this tag to change the font size of Latex formulas included
# as images in the HTML documentation. The default is 10. Note that
# when you change the font size after a successful doxygen run you need
# to manually remove any form_*.png images from the HTML output directory
# to force them to be regenerated.
FORMULA_FONTSIZE = 10
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked. If left blank `latex' will be used as the default command name.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
# If LATEX_HIDE_INDICES is set to YES then doxygen will not
# include the index chapters (such as File Index, Compound Index, etc.)
# in the output.
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimized for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using WORD or other
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
# then it will generate one additional man file for each entity
# documented in the real man page(s). These additional files
# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `xml' will be used as the default path.
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
# dump the program listings (including syntax highlighting
# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
# generate an AutoGen Definitions (see autogen.sf.net) file
# that captures the structure of the code including all
# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES Doxygen will
# generate a Perl module file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
# the necessary Makefile rules, Perl scripts and LaTeX code to be able
# to generate PDF and DVI output from the Perl module output.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
# nicely formatted so it can be parsed by a human reader.
# This is useful
# if you want to understand what is going on.
# On the other hand, if this
# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file
# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
# This is useful so different doxyrules.make files included by the same
# Makefile don't overwrite each other's variables.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_DEFINED tags.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed. To prevent a macro definition from being
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line, have an all uppercase name, and do not end with a semicolon. Such
# function macros are typically used for boiler-plate code, and will confuse
# the parser if not removed.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
# The TAGFILES option can be used to specify one or more tagfiles.
# Optionally an initial location of the external documentation
# can be added for each tagfile. The format of a tag file without
# this location is as follows:
#
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
#
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where "loc1" and "loc2" can be relative or absolute paths or
# URLs. If a location is present for each tag, the installdox tool
# does not have to be run to correct the links.
# Note that each tag file must have a unique name
# (where the name does NOT include the path)
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
# or super classes. Setting the tag to NO turns the diagrams off. Note that
# this option is superseded by the HAVE_DOT option below. This is only a
# fallback. It is recommended to install and use dot, since it yields more
# powerful graphs.
CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see
# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, a graph visualization
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = YES
# By default doxygen will write a font called FreeSans.ttf to the output
# directory and reference it in all dot files that doxygen generates. This
# font does not include all possible unicode characters however, so when you need
# these (or just want a differently looking font) you can specify the font name
# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
# which can be done by putting it in a standard location or by setting the
# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
# containing the font.
DOT_FONTNAME = FreeSans
# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
# The default size is 10pt.
DOT_FONTSIZE = 10
# By default doxygen will tell dot to use the output directory to look for the
# FreeSans.ttf font (which doxygen will put there itself). If you specify a
# different font using DOT_FONTNAME you can set the path where dot
# can find it using this tag.
DOT_FONTPATH =
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for groups, showing the direct groups dependencies
GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
UML_LOOK = NO
# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = NO
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
# tags are set to YES then doxygen will generate a graph for each documented
# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
# If the CALL_GRAPH and HAVE_DOT options are set to YES then
# doxygen will generate a call dependency graph for every global function
# or class method. Note that enabling this option will significantly increase
# the time of a run. So in most cases it will be better to enable call graphs
# for selected functions only using the \callgraph command.
CALL_GRAPH = NO
# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
# doxygen will generate a caller dependency graph for every global function
# or class method. Note that enabling this option will significantly increase
# the time of a run. So in most cases it will be better to enable caller
# graphs for selected functions only using the \callergraph command.
CALLER_GRAPH = NO
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
# then doxygen will show the dependencies a directory has on other directories
# in a graphical way. The dependency relations are determined by the #include
# relations between the files in the directories.
DIRECTORY_GRAPH = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.
DOT_IMAGE_FORMAT = png
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
# nodes that will be shown in the graph. If the number of nodes in a graph
# becomes larger than this value, doxygen will truncate the graph, which is
# visualized by representing a node as a red box. Note that doxygen if the
# number of direct children of the root node in a graph is already larger than
# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
DOT_GRAPH_MAX_NODES = 50
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
# graphs generated by dot. A depth value of 3 means that only nodes reachable
# from the root by following a path via at most 3 edges will be shown. Nodes
# that lay further from the root node will be omitted. Note that setting this
# option to 1 or 2 may greatly reduce the computation time needed for large
# code bases. Also note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not
# seem to support this out of the box. Warning: Depending on the platform used,
# enabling this option may lead to badly anti-aliased labels on the edges of
# a graph (i.e. they become hard to read).
DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10)
# support this, this feature is disabled by default.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermediate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Options related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = YES
pymodbus-2.1.0/doc/api/doxygen/build.py 0000664 0000000 0000000 00000002045 13355134677 0020037 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
'''
Doxygen API Builder
---------------------
'''
import os, shutil
def is_exe(path):
''' Returns if the program is executable
:param path: The path to the file
:return: True if it is, False otherwise
'''
return os.path.exists(path) and os.access(path, os.X_OK)
def which(program):
''' Check to see if an executable exists
:param program: The program to check for
:return: The full path of the executable or None if not found
'''
fpath, name = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
if which('doxygen') is not None:
print "Building Doxygen API Documentation"
os.system("doxygen .doxygen")
if os.path.exists('../../../build'):
shutil.move("html", "../../../build/doxygen")
else: print "Doxygen not available...not building"
pymodbus-2.1.0/doc/api/epydoc/ 0000775 0000000 0000000 00000000000 13355134677 0016173 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/api/epydoc/build.py 0000775 0000000 0000000 00000001702 13355134677 0017647 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
'''
Epydoc API Runner
------------------
Using pkg_resources, we attempt to see if epydoc is installed,
if so, we use its cli program to compile the documents
'''
try:
import sys, os, shutil
import pkg_resources
pkg_resources.require("epydoc")
from epydoc.cli import cli
sys.argv = '''epydoc.py pymodbus
--html --simple-term --quiet
--include-log
--graph=all
--docformat=plaintext
--debug
--exclude=._
--exclude=tests
--output=html/
'''.split()
#bugs in trunk for --docformat=restructuredtext
if not os.path.exists("./html"):
os.mkdir("./html")
print "Building Epydoc API Documentation"
cli()
if os.path.exists('../../../build'):
shutil.move("html", "../../../build/epydoc")
except Exception, ex:
import traceback,sys
traceback.print_exc(file=sys.stdout)
print "Epydoc not avaliable...not building"
pymodbus-2.1.0/doc/api/pydoc/ 0000775 0000000 0000000 00000000000 13355134677 0016026 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/api/pydoc/build.py 0000664 0000000 0000000 00000036374 13355134677 0017514 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pydoc sub-class for generating documentation for entire packages.
Taken from: http://pyopengl.sourceforge.net/pydoc/OpenGLContext.pydoc.pydoc2.html
Author: Mike Fletcher
"""
import logging
import pydoc, inspect, os, string, shutil
import sys, imp, os, stat, re, types, inspect
from repr import Repr
from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
_log = logging.getLogger(__name__)
def classify_class_attrs(cls):
"""Return list of attribute-descriptor tuples.
For each name in dir(cls), the return list contains a 4-tuple
with these elements:
0. The name (a string).
1. The kind of attribute this is, one of these strings:
'class method' created via classmethod()
'static method' created via staticmethod()
'property' created via property()
'method' any other flavor of method
'data' not a method
2. The class which defined this attribute (a class).
3. The object as obtained directly from the defining class's
__dict__, not via getattr. This is especially important for
data attributes: C.data is just a data object, but
C.__dict__['data'] may be a data descriptor with additional
info, like a __doc__ string.
Note: This version is patched to work with Zope Interface-bearing objects
"""
mro = inspect.getmro(cls)
names = dir(cls)
result = []
for name in names:
# Get the object associated with the name.
# Getting an obj from the __dict__ sometimes reveals more than
# using getattr. Static and class methods are dramatic examples.
if name in cls.__dict__:
obj = cls.__dict__[name]
else:
try:
obj = getattr(cls, name)
except AttributeError, err:
continue
# Figure out where it was defined.
homecls = getattr(obj, "__objclass__", None)
if homecls is None:
# search the dicts.
for base in mro:
if name in base.__dict__:
homecls = base
break
# Get the object again, in order to get it from the defining
# __dict__ instead of via getattr (if possible).
if homecls is not None and name in homecls.__dict__:
obj = homecls.__dict__[name]
# Also get the object via getattr.
obj_via_getattr = getattr(cls, name)
# Classify the object.
if isinstance(obj, staticmethod):
kind = "static method"
elif isinstance(obj, classmethod):
kind = "class method"
elif isinstance(obj, property):
kind = "property"
elif (inspect.ismethod(obj_via_getattr) or
inspect.ismethoddescriptor(obj_via_getattr)):
kind = "method"
else:
kind = "data"
result.append((name, kind, homecls, obj))
return result
inspect.classify_class_attrs = classify_class_attrs
class DefaultFormatter(pydoc.HTMLDoc):
def docmodule(self, object, name=None, mod=None, packageContext = None, *ignored):
"""Produce HTML documentation for a module object."""
name = object.__name__ # ignore the passed-in name
parts = split(name, '.')
links = []
for i in range(len(parts)-1):
links.append(
'%s' %
(join(parts[:i+1], '.'), parts[i]))
linkedname = join(links + parts[-1:], '.')
head = '%s' % linkedname
try:
path = inspect.getabsfile(object)
url = path
if sys.platform == 'win32':
import nturl2path
url = nturl2path.pathname2url(path)
filelink = '%s' % (url, path)
except TypeError:
filelink = '(built-in)'
info = []
if hasattr(object, '__version__'):
version = str(object.__version__)
if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
version = strip(version[11:-1])
info.append('version %s' % self.escape(version))
if hasattr(object, '__date__'):
info.append(self.escape(str(object.__date__)))
if info:
head = head + ' (%s)' % join(info, ', ')
result = self.heading(
head, '#ffffff', '#7799ee', 'index ' + filelink)
modules = inspect.getmembers(object, inspect.ismodule)
classes, cdict = [], {}
for key, value in inspect.getmembers(object, inspect.isclass):
if (inspect.getmodule(value) or object) is object:
classes.append((key, value))
cdict[key] = cdict[value] = '#' + key
for key, value in classes:
for base in value.__bases__:
key, modname = base.__name__, base.__module__
module = sys.modules.get(modname)
if modname != name and module and hasattr(module, key):
if getattr(module, key) is base:
if not cdict.has_key(key):
cdict[key] = cdict[base] = modname + '.html#' + key
funcs, fdict = [], {}
for key, value in inspect.getmembers(object, inspect.isroutine):
if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
funcs.append((key, value))
fdict[key] = '#-' + key
if inspect.isfunction(value): fdict[value] = fdict[key]
data = []
for key, value in inspect.getmembers(object, pydoc.isdata):
if key not in ['__builtins__', '__doc__']:
data.append((key, value))
doc = self.markup(pydoc.getdoc(object), self.preformat, fdict, cdict)
doc = doc and '%s' % doc
result = result + '
%s
\n' % doc
packageContext.clean ( classes, object )
packageContext.clean ( funcs, object )
packageContext.clean ( data, object )
if hasattr(object, '__path__'):
modpkgs = []
modnames = []
for file in os.listdir(object.__path__[0]):
path = os.path.join(object.__path__[0], file)
modname = inspect.getmodulename(file)
if modname and modname not in modnames:
modpkgs.append((modname, name, 0, 0))
modnames.append(modname)
elif pydoc.ispackage(path):
modpkgs.append((file, name, 1, 0))
modpkgs.sort()
contents = self.multicolumn(modpkgs, self.modpkglink)
## result = result + self.bigsection(
## 'Package Contents', '#ffffff', '#aa55cc', contents)
result = result + self.moduleSection( object, packageContext)
elif modules:
contents = self.multicolumn(
modules, lambda (key, value), s=self: s.modulelink(value))
result = result + self.bigsection(
'Modules', '#fffff', '#aa55cc', contents)
if classes:
classlist = map(lambda (key, value): value, classes)
contents = [
self.formattree(inspect.getclasstree(classlist, 1), name)]
for key, value in classes:
contents.append(self.document(value, key, name, fdict, cdict))
result = result + self.bigsection(
'Classes', '#ffffff', '#ee77aa', join(contents))
if funcs:
contents = []
for key, value in funcs:
contents.append(self.document(value, key, name, fdict, cdict))
result = result + self.bigsection(
'Functions', '#ffffff', '#eeaa77', join(contents))
if data:
contents = []
for key, value in data:
try:
contents.append(self.document(value, key))
except Exception, err:
pass
result = result + self.bigsection(
'Data', '#ffffff', '#55aa55', join(contents, ' \n'))
if hasattr(object, '__author__'):
contents = self.markup(str(object.__author__), self.preformat)
result = result + self.bigsection(
'Author', '#ffffff', '#7799ee', contents)
if hasattr(object, '__credits__'):
contents = self.markup(str(object.__credits__), self.preformat)
result = result + self.bigsection(
'Credits', '#ffffff', '#7799ee', contents)
return result
def classlink(self, object, modname):
"""Make a link for a class."""
name, module = object.__name__, sys.modules.get(object.__module__)
if hasattr(module, name) and getattr(module, name) is object:
return '%s' % (
module.__name__, name, name
)
return pydoc.classname(object, modname)
def moduleSection( self, object, packageContext ):
"""Create a module-links section for the given object (module)"""
modules = inspect.getmembers(object, inspect.ismodule)
packageContext.clean ( modules, object )
packageContext.recurseScan( modules )
if hasattr(object, '__path__'):
modpkgs = []
modnames = []
for file in os.listdir(object.__path__[0]):
path = os.path.join(object.__path__[0], file)
modname = inspect.getmodulename(file)
if modname and modname not in modnames:
modpkgs.append((modname, object.__name__, 0, 0))
modnames.append(modname)
elif pydoc.ispackage(path):
modpkgs.append((file, object.__name__, 1, 0))
modpkgs.sort()
# do more recursion here...
for (modname, name, ya,yo) in modpkgs:
packageContext.addInteresting( join( (object.__name__, modname), '.'))
items = []
for (modname, name, ispackage,isshadowed) in modpkgs:
try:
# get the actual module object...
## if modname == "events":
## import pdb
## pdb.set_trace()
module = pydoc.safeimport( "%s.%s"%(name,modname) )
description, documentation = pydoc.splitdoc( inspect.getdoc( module ))
if description:
items.append(
"""%s -- %s"""% (
self.modpkglink( (modname, name, ispackage, isshadowed) ),
description,
)
)
else:
items.append(
self.modpkglink( (modname, name, ispackage, isshadowed) )
)
except:
items.append(
self.modpkglink( (modname, name, ispackage, isshadowed) )
)
contents = string.join( items, ' ')
result = self.bigsection(
'Package Contents', '#ffffff', '#aa55cc', contents)
elif modules:
contents = self.multicolumn(
modules, lambda (key, value), s=self: s.modulelink(value))
result = self.bigsection(
'Modules', '#fffff', '#aa55cc', contents)
else:
result = ""
return result
class AlreadyDone(Exception):
pass
class PackageDocumentationGenerator:
"""A package document generator creates documentation
for an entire package using pydoc's machinery.
baseModules -- modules which will be included
and whose included and children modules will be
considered fair game for documentation
destinationDirectory -- the directory into which
the HTML documentation will be written
recursion -- whether to add modules which are
referenced by and/or children of base modules
exclusions -- a list of modules whose contents will
not be shown in any other module, commonly
such modules as OpenGL.GL, wxPython.wx etc.
recursionStops -- a list of modules which will
explicitly stop recursion (i.e. they will never
be included), even if they are children of base
modules.
formatter -- allows for passing in a custom formatter
see DefaultFormatter for sample implementation.
"""
def __init__ (
self, baseModules, destinationDirectory = ".",
recursion = 1, exclusions = (),
recursionStops = (),
formatter = None
):
self.destinationDirectory = os.path.abspath( destinationDirectory)
self.exclusions = {}
self.warnings = []
self.baseSpecifiers = {}
self.completed = {}
self.recursionStops = {}
self.recursion = recursion
for stop in recursionStops:
self.recursionStops[ stop ] = 1
self.pending = []
for exclusion in exclusions:
try:
self.exclusions[ exclusion ]= pydoc.locate ( exclusion)
except pydoc.ErrorDuringImport, value:
self.warn( """Unable to import the module %s which was specified as an exclusion module"""% (repr(exclusion)))
self.formatter = formatter or DefaultFormatter()
for base in baseModules:
self.addBase( base )
def warn( self, message ):
"""Warnings are used for recoverable, but not necessarily ignorable conditions"""
self.warnings.append (message)
def info (self, message):
"""Information/status report"""
_log.debug(message)
def addBase(self, specifier):
"""Set the base of the documentation set, only children of these modules will be documented"""
try:
self.baseSpecifiers [specifier] = pydoc.locate ( specifier)
self.pending.append (specifier)
except pydoc.ErrorDuringImport, value:
self.warn( """Unable to import the module %s which was specified as a base module"""% (repr(specifier)))
def addInteresting( self, specifier):
"""Add a module to the list of interesting modules"""
if self.checkScope( specifier):
self.pending.append (specifier)
else:
self.completed[ specifier] = 1
def checkScope (self, specifier):
"""Check that the specifier is "in scope" for the recursion"""
if not self.recursion:
return 0
items = string.split (specifier, ".")
stopCheck = items [:]
while stopCheck:
name = string.join(items, ".")
if self.recursionStops.get( name):
return 0
elif self.completed.get (name):
return 0
del stopCheck[-1]
while items:
if self.baseSpecifiers.get( string.join(items, ".")):
return 1
del items[-1]
# was not within any given scope
return 0
def process( self ):
"""Having added all of the base and/or interesting modules,
proceed to generate the appropriate documentation for each
module in the appropriate directory, doing the recursion
as we go."""
try:
while self.pending:
try:
if self.completed.has_key( self.pending[0] ):
raise AlreadyDone( self.pending[0] )
self.info( """Start %s"""% (repr(self.pending[0])))
object = pydoc.locate ( self.pending[0] )
self.info( """ ... found %s"""% (repr(object.__name__)))
except AlreadyDone:
pass
except pydoc.ErrorDuringImport, value:
self.info( """ ... FAILED %s"""% (repr( value)))
self.warn( """Unable to import the module %s"""% (repr(self.pending[0])))
except (SystemError, SystemExit), value:
self.info( """ ... FAILED %s"""% (repr( value)))
self.warn( """Unable to import the module %s"""% (repr(self.pending[0])))
except Exception, value:
self.info( """ ... FAILED %s"""% (repr( value)))
self.warn( """Unable to import the module %s"""% (repr(self.pending[0])))
else:
page = self.formatter.page(
pydoc.describe(object),
self.formatter.docmodule(
object,
object.__name__,
packageContext = self,
)
)
file = open (
os.path.join(
self.destinationDirectory,
self.pending[0] + ".html",
),
'w',
)
file.write(page)
file.close()
self.completed[ self.pending[0]] = object
del self.pending[0]
finally:
for item in self.warnings:
_log.info(item)
def clean (self, objectList, object):
"""callback from the formatter object asking us to remove
those items in the key, value pairs where the object is
imported from one of the excluded modules"""
for key, value in objectList[:]:
for excludeObject in self.exclusions.values():
if hasattr( excludeObject, key ) and excludeObject is not object:
if (
getattr( excludeObject, key) is value or
(hasattr( excludeObject, '__name__') and
excludeObject.__name__ == "Numeric"
)
):
objectList[:] = [ (k,o) for k,o in objectList if k != key ]
def recurseScan(self, objectList):
"""Process the list of modules trying to add each to the
list of interesting modules"""
for key, value in objectList:
self.addInteresting( value.__name__ )
#---------------------------------------------------------------------------#
# Main Runner
#---------------------------------------------------------------------------#
if __name__ == "__main__":
if not os.path.exists("./html"):
os.mkdir("./html")
print "Building Pydoc API Documentation"
PackageDocumentationGenerator(
baseModules = ['pymodbus', '__builtin__'],
destinationDirectory = "./html/",
exclusions = ['math', 'string', 'twisted'],
recursionStops = [],
).process ()
if os.path.exists('../../../build'):
shutil.move("html", "../../../build/pydoc")
pymodbus-2.1.0/doc/api/pydoctor/ 0000775 0000000 0000000 00000000000 13355134677 0016553 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/api/pydoctor/build.py 0000775 0000000 0000000 00000001440 13355134677 0020226 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
'''
Pydoctor API Runner
---------------------
Using pkg_resources, we attempt to see if pydoctor is installed,
if so, we use its cli program to compile the documents
'''
try:
import sys, os, shutil
import pkg_resources
pkg_resources.require("pydoctor")
from pydoctor.driver import main
sys.argv = '''pydoctor.py --quiet
--project-name=Pymodbus
--project-url=http://code.google.com/p/pymodbus/
--add-package=../../../pymodbus
--html-output=html
--html-write-function-pages --make-html'''.split()
print "Building Pydoctor API Documentation"
main(sys.argv[1:])
if os.path.exists('../../../build'):
shutil.move("html", "../../../build/pydoctor")
except: print "Pydoctor unavailable...not building"
pymodbus-2.1.0/doc/changelog.rst 0000664 0000000 0000000 00000000103 13355134677 0016612 0 ustar 00root root 0000000 0000000 ============
CHANGELOGS
============
.. include:: ../CHANGELOG.rst pymodbus-2.1.0/doc/conf.py 0000664 0000000 0000000 00000013732 13355134677 0015444 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
#
# PyModbus documentation build configuration file, created by
# sphinx-quickstart on Wed Dec 20 12:31:10 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
import recommonmark
from recommonmark.parser import CommonMarkParser
from recommonmark.transform import AutoStructify
from pymodbus import __version__
parent_dir = os.path.abspath(os.pardir)
# examples = os.path.join(parent_dir, "examples")
example_contrib = os.path.join(parent_dir, "examples/contrib")
example_common = os.path.join(parent_dir, "examples/common")
example_gui = os.path.join(parent_dir, "examples/gui")
sys.path.insert(0, os.path.abspath(os.pardir))
sys.path.append(example_common)
sys.path.append(example_contrib)
sys.path.append(example_gui)
# sys.path.extend([examples, example_common, example_contrib, example_gui])
# sys.path.insert(0, os.path.abspath('../'))
github_doc_root = 'https://github.com/riptideio/pymodbus/tree/master/doc/'
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc', 'recommonmark']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_parsers = {
'.md': CommonMarkParser,
}
source_suffix = ['.rst', '.md']
# source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'PyModbus'
copyright = u'2017, Sanjay'
author = u'Sanjay'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
'**': [
'relations.html', # needs 'show_related': True theme option to display
'searchbox.html',
]
}
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'PyModbusdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'PyModbus.tex', u'PyModbus Documentation',
u'Sanjay', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'pymodbus', u'PyModbus Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'PyModbus', u'PyModbus Documentation',
author, 'PyModbus', 'One line description of project.',
'Miscellaneous'),
]
def setup(app):
app.add_config_value('recommonmark_config', {
'url_resolver': lambda url: github_doc_root + url,
'auto_toc_tree_section': 'Contents',
}, True)
app.add_transform(AutoStructify)
pymodbus-2.1.0/doc/index.rst 0000664 0000000 0000000 00000001052 13355134677 0015776 0 ustar 00root root 0000000 0000000 .. PyModbus documentation master file, created by
sphinx-quickstart on Wed Dec 20 12:31:10 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to PyModbus's documentation!
====================================
.. toctree::
:maxdepth: 2
:caption: Contents:
readme.rst
changelog.rst
source/library/REPL
source/example/modules.rst
source/library/modules.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
pymodbus-2.1.0/doc/readme.rst 0000664 0000000 0000000 00000000032 13355134677 0016121 0 ustar 00root root 0000000 0000000 .. include:: ../README.rst pymodbus-2.1.0/doc/source/ 0000775 0000000 0000000 00000000000 13355134677 0015437 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/source/example/ 0000775 0000000 0000000 00000000000 13355134677 0017072 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/source/example/async_asyncio_client.rst 0000664 0000000 0000000 00000000307 13355134677 0024024 0 ustar 00root root 0000000 0000000 ==================================================
Async Asyncio Client Example
==================================================
.. literalinclude:: ../../../examples/common/async_asyncio_client.py pymodbus-2.1.0/doc/source/example/async_asyncio_serial_client.rst 0000664 0000000 0000000 00000000325 13355134677 0025363 0 ustar 00root root 0000000 0000000 ==================================================
Async Asyncio Serial Client Example
==================================================
.. literalinclude:: ../../../examples/common/async_asyncio_serial_client.py pymodbus-2.1.0/doc/source/example/async_tornado_client.rst 0000664 0000000 0000000 00000000307 13355134677 0024025 0 ustar 00root root 0000000 0000000 ==================================================
Async Tornado Client Example
==================================================
.. literalinclude:: ../../../examples/common/async_tornado_client.py pymodbus-2.1.0/doc/source/example/async_tornado_client_serial.rst 0000664 0000000 0000000 00000000325 13355134677 0025364 0 ustar 00root root 0000000 0000000 ==================================================
Async Tornado Client Serial Example
==================================================
.. literalinclude:: ../../../examples/common/async_tornado_client_serial.py pymodbus-2.1.0/doc/source/example/async_twisted_client.rst 0000664 0000000 0000000 00000000307 13355134677 0024042 0 ustar 00root root 0000000 0000000 ==================================================
Async Twisted Client Example
==================================================
.. literalinclude:: ../../../examples/common/async_twisted_client.py pymodbus-2.1.0/doc/source/example/async_twisted_client_serial.rst 0000664 0000000 0000000 00000000325 13355134677 0025401 0 ustar 00root root 0000000 0000000 ==================================================
Async Twisted Client Serial Example
==================================================
.. literalinclude:: ../../../examples/common/async_twisted_client_serial.py pymodbus-2.1.0/doc/source/example/asynchronous_asyncio_serial_client.rst 0000664 0000000 0000000 00000000344 13355134677 0027002 0 ustar 00root root 0000000 0000000 ==================================================
Asynchronous Asyncio Serial Client Example
==================================================
.. literalinclude:: ../../../examples/contrib/asynchronous_asyncio_serial_client.py pymodbus-2.1.0/doc/source/example/asynchronous_processor.rst 0000664 0000000 0000000 00000000313 13355134677 0024453 0 ustar 00root root 0000000 0000000 ==================================================
Asynchronous Processor Example
==================================================
.. literalinclude:: ../../../examples/common/asynchronous_processor.py pymodbus-2.1.0/doc/source/example/asynchronous_server.rst 0000664 0000000 0000000 00000000230 13355134677 0023740 0 ustar 00root root 0000000 0000000 ===========================
Asynchronous Server Example
===========================
.. literalinclude:: ../../../examples/common/asynchronous_server.py
pymodbus-2.1.0/doc/source/example/bcd_payload.rst 0000664 0000000 0000000 00000000266 13355134677 0022071 0 ustar 00root root 0000000 0000000 ==================================================
Bcd Payload Example
==================================================
.. literalinclude:: ../../../examples/contrib/bcd_payload.py pymodbus-2.1.0/doc/source/example/callback_server.rst 0000664 0000000 0000000 00000000217 13355134677 0022746 0 ustar 00root root 0000000 0000000 ===========================
Callback Server Example
===========================
.. literalinclude:: ../../../examples/common/callback_server.py pymodbus-2.1.0/doc/source/example/changing_framers.rst 0000664 0000000 0000000 00000000277 13355134677 0023127 0 ustar 00root root 0000000 0000000 ==================================================
Changing Framers Example
==================================================
.. literalinclude:: ../../../examples/common/changing_framers.py pymodbus-2.1.0/doc/source/example/concurrent_client.rst 0000664 0000000 0000000 00000000302 13355134677 0023337 0 ustar 00root root 0000000 0000000 ==================================================
Concurrent Client Example
==================================================
.. literalinclude:: ../../../examples/contrib/concurrent_client.py pymodbus-2.1.0/doc/source/example/custom_datablock.rst 0000664 0000000 0000000 00000000277 13355134677 0023150 0 ustar 00root root 0000000 0000000 ==================================================
Custom Datablock Example
==================================================
.. literalinclude:: ../../../examples/common/custom_datablock.py pymodbus-2.1.0/doc/source/example/custom_message.rst 0000664 0000000 0000000 00000000273 13355134677 0022644 0 ustar 00root root 0000000 0000000 ==================================================
Custom Message Example
==================================================
.. literalinclude:: ../../../examples/common/custom_message.py pymodbus-2.1.0/doc/source/example/dbstore_update_server.rst 0000664 0000000 0000000 00000000311 13355134677 0024211 0 ustar 00root root 0000000 0000000 ==================================================
Dbstore Update Server Example
==================================================
.. literalinclude:: ../../../examples/common/dbstore_update_server.py pymodbus-2.1.0/doc/source/example/gui_common.rst 0000664 0000000 0000000 00000000260 13355134677 0021756 0 ustar 00root root 0000000 0000000 ==================================================
Gui Common Example
==================================================
.. literalinclude:: ../../../examples/gui/gui_common.py pymodbus-2.1.0/doc/source/example/libmodbus_client.rst 0000664 0000000 0000000 00000000300 13355134677 0023133 0 ustar 00root root 0000000 0000000 ==================================================
Libmodbus Client Example
==================================================
.. literalinclude:: ../../../examples/contrib/libmodbus_client.py pymodbus-2.1.0/doc/source/example/message_generator.rst 0000664 0000000 0000000 00000000302 13355134677 0023311 0 ustar 00root root 0000000 0000000 ==================================================
Message Generator Example
==================================================
.. literalinclude:: ../../../examples/contrib/message_generator.py pymodbus-2.1.0/doc/source/example/message_parser.rst 0000664 0000000 0000000 00000000274 13355134677 0022627 0 ustar 00root root 0000000 0000000 ==================================================
Message Parser Example
==================================================
.. literalinclude:: ../../../examples/contrib/message_parser.py pymodbus-2.1.0/doc/source/example/modbus_logging.rst 0000664 0000000 0000000 00000000273 13355134677 0022625 0 ustar 00root root 0000000 0000000 ==================================================
Modbus Logging Example
==================================================
.. literalinclude:: ../../../examples/common/modbus_logging.py pymodbus-2.1.0/doc/source/example/modbus_mapper.rst 0000664 0000000 0000000 00000000272 13355134677 0022462 0 ustar 00root root 0000000 0000000 ==================================================
Modbus Mapper Example
==================================================
.. literalinclude:: ../../../examples/contrib/modbus_mapper.py pymodbus-2.1.0/doc/source/example/modbus_payload.rst 0000664 0000000 0000000 00000000273 13355134677 0022630 0 ustar 00root root 0000000 0000000 ==================================================
Modbus Payload Example
==================================================
.. literalinclude:: ../../../examples/common/modbus_payload.py pymodbus-2.1.0/doc/source/example/modbus_payload_server.rst 0000664 0000000 0000000 00000000311 13355134677 0024207 0 ustar 00root root 0000000 0000000 ==================================================
Modbus Payload Server Example
==================================================
.. literalinclude:: ../../../examples/common/modbus_payload_server.py pymodbus-2.1.0/doc/source/example/modbus_saver.rst 0000664 0000000 0000000 00000000270 13355134677 0022314 0 ustar 00root root 0000000 0000000 ==================================================
Modbus Saver Example
==================================================
.. literalinclude:: ../../../examples/contrib/modbus_saver.py pymodbus-2.1.0/doc/source/example/modbus_scraper.rst 0000664 0000000 0000000 00000000274 13355134677 0022637 0 ustar 00root root 0000000 0000000 ==================================================
Modbus Scraper Example
==================================================
.. literalinclude:: ../../../examples/contrib/modbus_scraper.py pymodbus-2.1.0/doc/source/example/modbus_simulator.rst 0000664 0000000 0000000 00000000300 13355134677 0023205 0 ustar 00root root 0000000 0000000 ==================================================
Modbus Simulator Example
==================================================
.. literalinclude:: ../../../examples/contrib/modbus_simulator.py pymodbus-2.1.0/doc/source/example/modicon_payload.rst 0000664 0000000 0000000 00000000276 13355134677 0022772 0 ustar 00root root 0000000 0000000 ==================================================
Modicon Payload Example
==================================================
.. literalinclude:: ../../../examples/contrib/modicon_payload.py pymodbus-2.1.0/doc/source/example/modules.rst 0000664 0000000 0000000 00000001572 13355134677 0021301 0 ustar 00root root 0000000 0000000 ===================
Examples
===================
===
.. toctree::
:maxdepth: 4
async_asyncio_client
async_asyncio_serial_client
async_tornado_client
async_tornado_client_serial
async_twisted_client
async_twisted_client_serial
asynchronous_processor
asynchronous_server
callback_server
changing_framers
custom_datablock
custom_message
dbstore_update_server
modbus_logging
modbus_payload
modbus_payload_server
performance
synchronous_client
synchronous_client_ext
synchronous_server
updating_server
asynchronous_asyncio_serial_client
bcd_payload
concurrent_client
libmodbus_client
message_generator
message_parser
modbus_mapper
modbus_saver
modbus_scraper
modbus_simulator
modicon_payload
remote_server_context
serial_forwarder
sunspec_client
thread_safe_datastore
gui_common
pymodbus-2.1.0/doc/source/example/performance.rst 0000664 0000000 0000000 00000000166 13355134677 0022130 0 ustar 00root root 0000000 0000000 ==================
performance module
==================
.. literalinclude:: ../../../examples/common/performance.py
pymodbus-2.1.0/doc/source/example/remote_server_context.rst 0000664 0000000 0000000 00000000312 13355134677 0024245 0 ustar 00root root 0000000 0000000 ==================================================
Remote Server Context Example
==================================================
.. literalinclude:: ../../../examples/contrib/remote_server_context.py pymodbus-2.1.0/doc/source/example/serial_forwarder.rst 0000664 0000000 0000000 00000000300 13355134677 0023147 0 ustar 00root root 0000000 0000000 ==================================================
Serial Forwarder Example
==================================================
.. literalinclude:: ../../../examples/contrib/serial_forwarder.py pymodbus-2.1.0/doc/source/example/sunspec_client.rst 0000664 0000000 0000000 00000000274 13355134677 0022645 0 ustar 00root root 0000000 0000000 ==================================================
Sunspec Client Example
==================================================
.. literalinclude:: ../../../examples/contrib/sunspec_client.py pymodbus-2.1.0/doc/source/example/synchronous_client.rst 0000664 0000000 0000000 00000000303 13355134677 0023550 0 ustar 00root root 0000000 0000000 ==================================================
Synchronous Client Example
==================================================
.. literalinclude:: ../../../examples/common/synchronous_client.py pymodbus-2.1.0/doc/source/example/synchronous_client_ext.rst 0000664 0000000 0000000 00000000313 13355134677 0024431 0 ustar 00root root 0000000 0000000 ==================================================
Synchronous Client Ext Example
==================================================
.. literalinclude:: ../../../examples/common/synchronous_client_ext.py pymodbus-2.1.0/doc/source/example/synchronous_server.rst 0000664 0000000 0000000 00000000303 13355134677 0023600 0 ustar 00root root 0000000 0000000 ==================================================
Synchronous Server Example
==================================================
.. literalinclude:: ../../../examples/common/synchronous_server.py pymodbus-2.1.0/doc/source/example/thread_safe_datastore.rst 0000664 0000000 0000000 00000000312 13355134677 0024133 0 ustar 00root root 0000000 0000000 ==================================================
Thread Safe Datastore Example
==================================================
.. literalinclude:: ../../../examples/contrib/thread_safe_datastore.py pymodbus-2.1.0/doc/source/example/updating_server.rst 0000664 0000000 0000000 00000000275 13355134677 0023031 0 ustar 00root root 0000000 0000000 ==================================================
Updating Server Example
==================================================
.. literalinclude:: ../../../examples/common/updating_server.py pymodbus-2.1.0/doc/source/library/ 0000775 0000000 0000000 00000000000 13355134677 0017103 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/doc/source/library/REPL.md 0000664 0000000 0000000 00000034715 13355134677 0020201 0 ustar 00root root 0000000 0000000 # Pymodbus REPL
## Dependencies
Depends on [prompt_toolkit](https://python-prompt-toolkit.readthedocs.io/en/stable/index.html) and [click](http://click.pocoo.org/6/quickstart/)
Install dependencies
```
$ pip install click prompt_toolkit --upgarde
```
Or
Install pymodbus with repl support
```
$ pip install pymodbus[repl] --upgrade
```
## Usage Instructions
RTU and TCP are supported as of now
```
bash-3.2$ pymodbus.console
Usage: pymodbus.console [OPTIONS] COMMAND [ARGS]...
Options:
--version Show the version and exit.
--verbose Verbose logs
--support-diag Support Diagnostic messages
--help Show this message and exit.
Commands:
serial
tcp
```
TCP Options
```
bash-3.2$ pymodbus.console tcp --help
Usage: pymodbus.console tcp [OPTIONS]
Options:
--host TEXT Modbus TCP IP
--port INTEGER Modbus TCP port
--help Show this message and exit.
```
SERIAL Options
```
bash-3.2$ pymodbus.console serial --help
Usage: pymodbus.console serial [OPTIONS]
Options:
--method TEXT Modbus Serial Mode (rtu/ascii)
--port TEXT Modbus RTU port
--baudrate INTEGER Modbus RTU serial baudrate to use. Defaults to 9600
--bytesize [5|6|7|8] Modbus RTU serial Number of data bits. Possible
values: FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS.
Defaults to 8
--parity [N|E|O|M|S] Modbus RTU serial parity. Enable parity checking.
Possible values: PARITY_NONE, PARITY_EVEN, PARITY_ODD
PARITY_MARK, PARITY_SPACE. Default to 'N'
--stopbits [1|1.5|2] Modbus RTU serial stop bits. Number of stop bits.
Possible values: STOPBITS_ONE,
STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO. Default to '1'
--xonxoff INTEGER Modbus RTU serial xonxoff. Enable software flow
control.Defaults to 0
--rtscts INTEGER Modbus RTU serial rtscts. Enable hardware (RTS/CTS)
flow control. Defaults to 0
--dsrdtr INTEGER Modbus RTU serial dsrdtr. Enable hardware (DSR/DTR)
flow control. Defaults to 0
--timeout FLOAT Modbus RTU serial read timeout. Defaults to 0.025 sec
--write-timeout FLOAT Modbus RTU serial write timeout. Defaults to 2 sec
--help Show this message and exit.
```
To view all available commands type `help`
TCP
```
$ pymodbus.console tcp --host 192.168.128.126 --port 5020
> help
Available commands:
client.change_ascii_input_delimiter Diagnostic sub command, Change message delimiter for future requests.
client.clear_counters Diagnostic sub command, Clear all counters and diag registers.
client.clear_overrun_count Diagnostic sub command, Clear over run counter.
client.close Closes the underlying socket connection
client.connect Connect to the modbus tcp server
client.debug_enabled Returns a boolean indicating if debug is enabled.
client.force_listen_only_mode Diagnostic sub command, Forces the addressed remote device to its Listen Only Mode.
client.get_clear_modbus_plus Diagnostic sub command, Get or clear stats of remote modbus plus device.
client.get_com_event_counter Read status word and an event count from the remote device's communication event counter.
client.get_com_event_log Read status word, event count, message count, and a field of event bytes from the remote device.
client.host Read Only!
client.idle_time Bus Idle Time to initiate next transaction
client.is_socket_open Check whether the underlying socket/serial is open or not.
client.last_frame_end Read Only!
client.mask_write_register Mask content of holding register at `address` with `and_mask` and `or_mask`.
client.port Read Only!
client.read_coils Reads `count` coils from a given slave starting at `address`.
client.read_device_information Read the identification and additional information of remote slave.
client.read_discrete_inputs Reads `count` number of discrete inputs starting at offset `address`.
client.read_exception_status Read the contents of eight Exception Status outputs in a remote device.
client.read_holding_registers Read `count` number of holding registers starting at `address`.
client.read_input_registers Read `count` number of input registers starting at `address`.
client.readwrite_registers Read `read_count` number of holding registers starting at `read_address` and write `write_registers` starting at `write_address`.
client.report_slave_id Report information about remote slave ID.
client.restart_comm_option Diagnostic sub command, initialize and restart remote devices serial interface and clear all of its communications event counters .
client.return_bus_com_error_count Diagnostic sub command, Return count of CRC errors received by remote slave.
client.return_bus_exception_error_count Diagnostic sub command, Return count of Modbus exceptions returned by remote slave.
client.return_bus_message_count Diagnostic sub command, Return count of message detected on bus by remote slave.
client.return_diagnostic_register Diagnostic sub command, Read 16-bit diagnostic register.
client.return_iop_overrun_count Diagnostic sub command, Return count of iop overrun errors by remote slave.
client.return_query_data Diagnostic sub command , Loop back data sent in response.
client.return_slave_bus_char_overrun_count Diagnostic sub command, Return count of messages not handled by remote slave due to character overrun condition.
client.return_slave_busy_count Diagnostic sub command, Return count of server busy exceptions sent by remote slave.
client.return_slave_message_count Diagnostic sub command, Return count of messages addressed to remote slave.
client.return_slave_no_ack_count Diagnostic sub command, Return count of NO ACK exceptions sent by remote slave.
client.return_slave_no_response_count Diagnostic sub command, Return count of No responses by remote slave.
client.silent_interval Read Only!
client.state Read Only!
client.timeout Read Only!
client.write_coil Write `value` to coil at `address`.
client.write_coils Write `value` to coil at `address`.
client.write_register Write `value` to register at `address`.
client.write_registers Write list of `values` to registers starting at `address`.
```
SERIAL
```
$ pymodbus.console serial --port /dev/ttyUSB0 --baudrate 19200 --timeout 2
> help
Available commands:
client.baudrate Read Only!
client.bytesize Read Only!
client.change_ascii_input_delimiter Diagnostic sub command, Change message delimiter for future requests.
client.clear_counters Diagnostic sub command, Clear all counters and diag registers.
client.clear_overrun_count Diagnostic sub command, Clear over run counter.
client.close Closes the underlying socket connection
client.connect Connect to the modbus serial server
client.debug_enabled Returns a boolean indicating if debug is enabled.
client.force_listen_only_mode Diagnostic sub command, Forces the addressed remote device to its Listen Only Mode.
client.get_baudrate Serial Port baudrate.
client.get_bytesize Number of data bits.
client.get_clear_modbus_plus Diagnostic sub command, Get or clear stats of remote modbus plus device.
client.get_com_event_counter Read status word and an event count from the remote device's communication event counter.
client.get_com_event_log Read status word, event count, message count, and a field of event bytes from the remote device.
client.get_parity Enable Parity Checking.
client.get_port Serial Port.
client.get_serial_settings Gets Current Serial port settings.
client.get_stopbits Number of stop bits.
client.get_timeout Serial Port Read timeout.
client.idle_time Bus Idle Time to initiate next transaction
client.inter_char_timeout Read Only!
client.is_socket_open c l i e n t . i s s o c k e t o p e n
client.mask_write_register Mask content of holding register at `address` with `and_mask` and `or_mask`.
client.method Read Only!
client.parity Read Only!
client.port Read Only!
client.read_coils Reads `count` coils from a given slave starting at `address`.
client.read_device_information Read the identification and additional information of remote slave.
client.read_discrete_inputs Reads `count` number of discrete inputs starting at offset `address`.
client.read_exception_status Read the contents of eight Exception Status outputs in a remote device.
client.read_holding_registers Read `count` number of holding registers starting at `address`.
client.read_input_registers Read `count` number of input registers starting at `address`.
client.readwrite_registers Read `read_count` number of holding registers starting at `read_address` and write `write_registers` starting at `write_address`.
client.report_slave_id Report information about remote slave ID.
client.restart_comm_option Diagnostic sub command, initialize and restart remote devices serial interface and clear all of its communications event counters .
client.return_bus_com_error_count Diagnostic sub command, Return count of CRC errors received by remote slave.
client.return_bus_exception_error_count Diagnostic sub command, Return count of Modbus exceptions returned by remote slave.
client.return_bus_message_count Diagnostic sub command, Return count of message detected on bus by remote slave.
client.return_diagnostic_register Diagnostic sub command, Read 16-bit diagnostic register.
client.return_iop_overrun_count Diagnostic sub command, Return count of iop overrun errors by remote slave.
client.return_query_data Diagnostic sub command , Loop back data sent in response.
client.return_slave_bus_char_overrun_count Diagnostic sub command, Return count of messages not handled by remote slave due to character overrun condition.
client.return_slave_busy_count Diagnostic sub command, Return count of server busy exceptions sent by remote slave.
client.return_slave_message_count Diagnostic sub command, Return count of messages addressed to remote slave.
client.return_slave_no_ack_count Diagnostic sub command, Return count of NO ACK exceptions sent by remote slave.
client.return_slave_no_response_count Diagnostic sub command, Return count of No responses by remote slave.
client.set_baudrate Baudrate setter.
client.set_bytesize Byte size setter.
client.set_parity Parity Setter.
client.set_port Serial Port setter.
client.set_stopbits Stop bit setter.
client.set_timeout Read timeout setter.
client.silent_interval Read Only!
client.state Read Only!
client.stopbits Read Only!
client.timeout Read Only!
client.write_coil Write `value` to coil at `address`.
client.write_coils Write `value` to coil at `address`.
client.write_register Write `value` to register at `address`.
client.write_registers Write list of `values` to registers starting at `address`.
result.decode Decode the register response to known formatters.
result.raw Return raw result dict.
```
Every command has auto suggetion on the arguments supported , supply arg and value are to be supplied in `arg=val` format.
```
> client.read_holding_registers count=4 address=9 unit=1
{
"registers": [
60497,
47134,
34091,
15424
]
}
```
The last result could be accessed with `result.raw` command
```
> result.raw
{
"registers": [
15626,
55203,
28733,
18368
]
}
```
For Holding and Input register reads, the decoded value could be viewed with `result.decode`
```
> result.decode word_order=little byte_order=little formatters=float64
28.17
>
```
Client settings could be retrieved and altered as well.
```
> # For serial settings
> # Check the serial mode
> client.method
"rtu"
> client.get_serial_settings
{
"t1.5": 0.00171875,
"baudrate": 9600,
"read timeout": 0.5,
"port": "/dev/ptyp0",
"t3.5": 0.00401,
"bytesize": 8,
"parity": "N",
"stopbits": 1.0
}
> client.set_timeout value=1
null
> client.get_timeout
1.0
> client.get_serial_settings
{
"t1.5": 0.00171875,
"baudrate": 9600,
"read timeout": 1.0,
"port": "/dev/ptyp0",
"t3.5": 0.00401,
"bytesize": 8,
"parity": "N",
"stopbits": 1.0
}
```
## DEMO
[](https://asciinema.org/a/y1xOk7lm59U1bRBE2N1pDIj2o)
[](https://asciinema.org/a/edUqZN77fdjxL2toisiilJNwI)
pymodbus-2.1.0/doc/source/library/modules.rst 0000664 0000000 0000000 00000000075 13355134677 0021307 0 ustar 00root root 0000000 0000000 Pymodbus
========
.. toctree::
:maxdepth: 4
pymodbus
pymodbus-2.1.0/doc/source/library/pymodbus.client.async.asyncio.rst 0000664 0000000 0000000 00000000273 13355134677 0025536 0 ustar 00root root 0000000 0000000 pymodbus\.client\.async\.asyncio package
========================================
.. automodule:: pymodbus.client.async.asyncio
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.client.async.factory.rst 0000664 0000000 0000000 00000001461 13355134677 0025540 0 ustar 00root root 0000000 0000000 pymodbus\.client\.async\.factory package
========================================
.. automodule:: pymodbus.client.async.factory
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
pymodbus\.client\.async\.factory\.serial module
-----------------------------------------------
.. automodule:: pymodbus.client.async.factory.serial
:members:
:undoc-members:
:show-inheritance:
pymodbus\.client\.async\.factory\.tcp module
--------------------------------------------
.. automodule:: pymodbus.client.async.factory.tcp
:members:
:undoc-members:
:show-inheritance:
pymodbus\.client\.async\.factory\.udp module
--------------------------------------------
.. automodule:: pymodbus.client.async.factory.udp
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.client.async.rst 0000664 0000000 0000000 00000002124 13355134677 0024067 0 ustar 00root root 0000000 0000000 pymodbus\.client\.async package
===============================
.. automodule:: pymodbus.client.async
:members:
:undoc-members:
:show-inheritance:
Subpackages
-----------
.. toctree::
pymodbus.client.async.asyncio
pymodbus.client.async.factory
pymodbus.client.async.schedulers
pymodbus.client.async.tornado
pymodbus.client.async.twisted
Submodules
----------
pymodbus\.client\.async\.serial module
--------------------------------------
.. automodule:: pymodbus.client.async.serial
:members:
:undoc-members:
:show-inheritance:
pymodbus\.client\.async\.tcp module
-----------------------------------
.. automodule:: pymodbus.client.async.tcp
:members:
:undoc-members:
:show-inheritance:
pymodbus\.client\.async\.thread module
--------------------------------------
.. automodule:: pymodbus.client.async.thread
:members:
:undoc-members:
:show-inheritance:
pymodbus\.client\.async\.udp module
-----------------------------------
.. automodule:: pymodbus.client.async.udp
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.client.async.schedulers.rst 0000664 0000000 0000000 00000000304 13355134677 0026225 0 ustar 00root root 0000000 0000000 pymodbus\.client\.async\.schedulers package
===========================================
.. automodule:: pymodbus.client.async.schedulers
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.client.async.tornado.rst 0000664 0000000 0000000 00000000273 13355134677 0025537 0 ustar 00root root 0000000 0000000 pymodbus\.client\.async\.tornado package
========================================
.. automodule:: pymodbus.client.async.tornado
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.client.async.twisted.rst 0000664 0000000 0000000 00000000273 13355134677 0025554 0 ustar 00root root 0000000 0000000 pymodbus\.client\.async\.twisted package
========================================
.. automodule:: pymodbus.client.async.twisted
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.client.rst 0000664 0000000 0000000 00000001045 13355134677 0022754 0 ustar 00root root 0000000 0000000 pymodbus\.client package
========================
.. automodule:: pymodbus.client
:members:
:undoc-members:
:show-inheritance:
Subpackages
-----------
.. toctree::
pymodbus.client.async
Submodules
----------
pymodbus\.client\.common module
-------------------------------
.. automodule:: pymodbus.client.common
:members:
:undoc-members:
:show-inheritance:
pymodbus\.client\.sync module
-----------------------------
.. automodule:: pymodbus.client.sync
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.datastore.database.rst 0000664 0000000 0000000 00000001217 13355134677 0025230 0 ustar 00root root 0000000 0000000 pymodbus\.datastore\.database package
=====================================
.. automodule:: pymodbus.datastore.database
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
pymodbus\.datastore\.database\.redis\_datastore module
------------------------------------------------------
.. automodule:: pymodbus.datastore.database.redis_datastore
:members:
:undoc-members:
:show-inheritance:
pymodbus\.datastore\.database\.sql\_datastore module
----------------------------------------------------
.. automodule:: pymodbus.datastore.database.sql_datastore
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.datastore.rst 0000664 0000000 0000000 00000001367 13355134677 0023473 0 ustar 00root root 0000000 0000000 pymodbus\.datastore package
===========================
.. automodule:: pymodbus.datastore
:members:
:undoc-members:
:show-inheritance:
Subpackages
-----------
.. toctree::
pymodbus.datastore.database
Submodules
----------
pymodbus\.datastore\.context module
-----------------------------------
.. automodule:: pymodbus.datastore.context
:members:
:undoc-members:
:show-inheritance:
pymodbus\.datastore\.remote module
----------------------------------
.. automodule:: pymodbus.datastore.remote
:members:
:undoc-members:
:show-inheritance:
pymodbus\.datastore\.store module
---------------------------------
.. automodule:: pymodbus.datastore.store
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.framer.rst 0000664 0000000 0000000 00000001626 13355134677 0022757 0 ustar 00root root 0000000 0000000 pymodbus\.framer package
========================
Submodules
----------
pymodbus\.framer\.ascii_framer module
-------------------------------------
.. automodule:: pymodbus.framer.ascii_framer
:members:
:undoc-members:
:show-inheritance:
pymodbus\.framer\.binary_framer module
--------------------------------------
.. automodule:: pymodbus.framer.binary_framer
:members:
:undoc-members:
:show-inheritance:
pymodbus\.framer\.rtu_framer module
-----------------------------------
.. automodule:: pymodbus.framer.rtu_framer
:members:
:undoc-members:
:show-inheritance:
pymodbus\.framer\.socket_framer module
--------------------------------------
.. automodule:: pymodbus.framer.socket_framer
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: pymodbus.framer
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.internal.rst 0000664 0000000 0000000 00000000531 13355134677 0023311 0 ustar 00root root 0000000 0000000 pymodbus\.internal package
==========================
.. automodule:: pymodbus.internal
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
pymodbus\.internal\.ptwisted module
-----------------------------------
.. automodule:: pymodbus.internal.ptwisted
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.repl.rst 0000664 0000000 0000000 00000001455 13355134677 0022445 0 ustar 00root root 0000000 0000000 pymodbus\.repl package
==========================
.. automodule:: pymodbus.repl
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
pymodbus\.repl\.client module
-----------------------------------
.. automodule:: pymodbus.repl.client
:members:
:undoc-members:
:show-inheritance:
pymodbus\.repl\.completer module
-----------------------------------
.. automodule:: pymodbus.repl.completer
:members:
:undoc-members:
:show-inheritance:
pymodbus\.repl\.helper module
-----------------------------------
.. automodule:: pymodbus.repl.helper
:members:
:undoc-members:
:show-inheritance:
pymodbus\.repl\.main module
-----------------------------------
.. automodule:: pymodbus.repl.main
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.rst 0000664 0000000 0000000 00000006503 13355134677 0021503 0 ustar 00root root 0000000 0000000 pymodbus package
================
.. automodule:: pymodbus
:members:
:undoc-members:
:show-inheritance:
Subpackages
-----------
.. toctree::
pymodbus.client
pymodbus.datastore
pymodbus.framer
pymodbus.internal
pymodbus.server
pymodbus.repl
Submodules
----------
pymodbus\.bit\_read\_message module
-----------------------------------
.. automodule:: pymodbus.bit_read_message
:members:
:undoc-members:
:show-inheritance:
pymodbus\.bit\_write\_message module
------------------------------------
.. automodule:: pymodbus.bit_write_message
:members:
:undoc-members:
:show-inheritance:
pymodbus\.compat module
-----------------------
.. automodule:: pymodbus.compat
:members:
:undoc-members:
:show-inheritance:
pymodbus\.constants module
--------------------------
.. automodule:: pymodbus.constants
:members:
:undoc-members:
:show-inheritance:
pymodbus\.device module
-----------------------
.. automodule:: pymodbus.device
:members:
:undoc-members:
:show-inheritance:
pymodbus\.diag\_message module
------------------------------
.. automodule:: pymodbus.diag_message
:members:
:undoc-members:
:show-inheritance:
pymodbus\.events module
-----------------------
.. automodule:: pymodbus.events
:members:
:undoc-members:
:show-inheritance:
pymodbus\.exceptions module
---------------------------
.. automodule:: pymodbus.exceptions
:members:
:undoc-members:
:show-inheritance:
pymodbus\.factory module
------------------------
.. automodule:: pymodbus.factory
:members:
:undoc-members:
:show-inheritance:
pymodbus\.file\_message module
------------------------------
.. automodule:: pymodbus.file_message
:members:
:undoc-members:
:show-inheritance:
pymodbus\.interfaces module
---------------------------
.. automodule:: pymodbus.interfaces
:members:
:undoc-members:
:show-inheritance:
pymodbus\.mei\_message module
-----------------------------
.. automodule:: pymodbus.mei_message
:members:
:undoc-members:
:show-inheritance:
pymodbus\.other\_message module
-------------------------------
.. automodule:: pymodbus.other_message
:members:
:undoc-members:
:show-inheritance:
pymodbus\.payload module
------------------------
.. automodule:: pymodbus.payload
:members:
:undoc-members:
:show-inheritance:
pymodbus\.pdu module
--------------------
.. automodule:: pymodbus.pdu
:members:
:undoc-members:
:show-inheritance:
pymodbus\.register\_read\_message module
----------------------------------------
.. automodule:: pymodbus.register_read_message
:members:
:undoc-members:
:show-inheritance:
pymodbus\.register\_write\_message module
-----------------------------------------
.. automodule:: pymodbus.register_write_message
:members:
:undoc-members:
:show-inheritance:
pymodbus\.transaction module
----------------------------
.. automodule:: pymodbus.transaction
:members:
:undoc-members:
:show-inheritance:
pymodbus\.utilities module
--------------------------
.. automodule:: pymodbus.utilities
:members:
:undoc-members:
:show-inheritance:
pymodbus\.version module
------------------------
.. automodule:: pymodbus.version
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/doc/source/library/pymodbus.server.rst 0000664 0000000 0000000 00000000740 13355134677 0023005 0 ustar 00root root 0000000 0000000 pymodbus\.server package
========================
.. automodule:: pymodbus.server
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
pymodbus\.server\.async module
------------------------------
.. automodule:: pymodbus.server.async
:members:
:undoc-members:
:show-inheritance:
pymodbus\.server\.sync module
-----------------------------
.. automodule:: pymodbus.server.sync
:members:
:undoc-members:
:show-inheritance:
pymodbus-2.1.0/examples/ 0000775 0000000 0000000 00000000000 13355134677 0015210 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/examples/common/ 0000775 0000000 0000000 00000000000 13355134677 0016500 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/examples/common/README.rst 0000664 0000000 0000000 00000007374 13355134677 0020202 0 ustar 00root root 0000000 0000000 ============================================================
Modbus Implementations
============================================================
There are a few reference implementations that you can use
to test modbus serial
------------------------------------------------------------
pymodbus
------------------------------------------------------------
You can use pymodbus as a testing server by simply modifying
one of the run scripts supplied here. There is an
asynchronous version and a synchronous version (that really
differ in how mnay dependencies you are willing to have).
Regardless of which one you choose, they can be started
quite easily::
./asynchronous-server.py
./synchronous-server.py
Currently, each version has implementations of the following:
- modbus tcp
- modbus udp
- modbus udp binary
- modbus ascii serial
- modbus ascii rtu
------------------------------------------------------------
Modbus Driver
------------------------------------------------------------
Included are reference implementations of a modbus client
and server using the modbus driver library (as well as
the relevant source code). Both programs have a wealth of
options and can be used to test either side of your
application::
tools/reference/diagslave -h # (server)
tools/reference/modpoll -h # (client)
------------------------------------------------------------
jamod
------------------------------------------------------------
Jamod is a complete modbus implementation for the java jvm.
Included are a few simple reference servers using the
library, however, a great deal more can be produced using
it. I have not tested it, however, it may even be possible
to use this library in conjunction with jython to interop
between your python code and this library:
* http://jamod.sourceforge.net/
------------------------------------------------------------
nmodbus
------------------------------------------------------------
Although there is not any code included in this package,
nmodbus is a complete implementation of the modbus protocol
for the .net clr. The site has a number of examples that can
be tuned for your testing needs:
* http://code.google.com/p/nmodbus/
============================================================
Serial Loopback Testing
============================================================
In order to test the serial implementations, one needs to
create a loopback connection (virtual serial port). This can
be done in a number of ways.
------------------------------------------------------------
Linux
------------------------------------------------------------
For linux, there are three ways that are included with this
distribution.
One is to use the socat utility. The following will get one
going quickly::
sudo apt-get install socat
sudo socat PTY,link=/dev/pts/13, PTY,link=/dev/pts/14
# connect the master to /dev/pts/13
# connect the client to /dev/pts/14
Next, you can include the loopback kernel driver included in
the tools/nullmodem/linux directory::
sudo ./run
------------------------------------------------------------
Windows
------------------------------------------------------------
For Windows, simply use the com2com application that is in
the directory tools/nullmodem/windows. Instructions are
included in the Readme.txt.
------------------------------------------------------------
Generic
------------------------------------------------------------
For most unix based systems, there is a simple virtual serial
forwarding application in the tools/nullmodem/ directory::
make run
# connect the master to the master output
# connect the client to the client output
Or for a tried and true method, simply connect a null modem
cable between two of your serial ports and then simply reference
those.
pymodbus-2.1.0/examples/common/async_asyncio_client.py 0000664 0000000 0000000 00000020120 13355134677 0023245 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Asynchronous Client Examples
--------------------------------------------------------------------------
The following is an example of how to use the asynchronous modbus
client implementation from pymodbus with ayncio.
The example is only valid on Python3.4 and above
"""
from pymodbus.compat import IS_PYTHON3, PYTHON_VERSION
if IS_PYTHON3 and PYTHON_VERSION >= (3, 4):
import asyncio
import logging
# ----------------------------------------------------------------------- #
# Import the required async client
# ----------------------------------------------------------------------- #
from pymodbus.client.async.tcp import AsyncModbusTCPClient as ModbusClient
# from pymodbus.client.async.udp import (
# AsyncModbusUDPClient as ModbusClient)
from pymodbus.client.async import schedulers
else:
import sys
sys.stderr("This example needs to be run only on python 3.4 and above")
sys.exit(1)
from threading import Thread
import time
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# specify slave to query
# --------------------------------------------------------------------------- #
# The slave to query is specified in an optional parameter for each
# individual request. This can be done by specifying the `unit` parameter
# which defaults to `0x00`
# --------------------------------------------------------------------------- #
UNIT = 0x01
async def start_async_test(client):
# ----------------------------------------------------------------------- #
# specify slave to query
# ----------------------------------------------------------------------- #
# The slave to query is specified in an optional parameter for each
# individual request. This can be done by specifying the `unit` parameter
# which defaults to `0x00`
# ----------------------------------------------------------------------- #
log.debug("Reading Coils")
rr = await client.read_coils(1, 1, unit=0x01)
# ----------------------------------------------------------------------- #
# example requests
# ----------------------------------------------------------------------- #
# simply call the methods that you would like to use. An example session
# is displayed below along with some assert checks. Note that some modbus
# implementations differentiate holding/input discrete/coils and as such
# you will not be able to write to these, therefore the starting values
# are not known to these tests. Furthermore, some use the same memory
# blocks for the two sets, so a change to one is a change to the other.
# Keep both of these cases in mind when testing as the following will
# _only_ pass with the supplied async modbus server (script supplied).
# ----------------------------------------------------------------------- #
log.debug("Write to a Coil and read back")
rq = await client.write_coil(0, True, unit=UNIT)
rr = await client.read_coils(0, 1, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.bits[0] == True) # test the expected value
log.debug("Write to multiple coils and read back- test 1")
rq = await client.write_coils(1, [True]*8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
rr = await client.read_coils(1, 21, unit=UNIT)
assert(rr.function_code < 0x80) # test that we are not an error
resp = [True]*21
# If the returned output quantity is not a multiple of eight,
# the remaining bits in the final data byte will be padded with zeros
# (toward the high order end of the byte).
resp.extend([False]*3)
assert(rr.bits == resp) # test the expected value
log.debug("Write to multiple coils and read back - test 2")
rq = await client.write_coils(1, [False]*8, unit=UNIT)
rr = await client.read_coils(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.bits == [False]*8) # test the expected value
log.debug("Read discrete inputs")
rr = await client.read_discrete_inputs(0, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
log.debug("Write to a holding register and read back")
rq = await client.write_register(1, 10, unit=UNIT)
rr = await client.read_holding_registers(1, 1, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.registers[0] == 10) # test the expected value
log.debug("Write to multiple holding registers and read back")
rq = await client.write_registers(1, [10]*8, unit=UNIT)
rr = await client.read_holding_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.registers == [10]*8) # test the expected value
log.debug("Read input registers")
rr = await client.read_input_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
log.debug("Read write registeres simulataneously")
rq = await client.readwrite_registers(unit=UNIT, **arguments)
rr = await client.read_holding_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rq.registers == [20]*8) # test the expected value
assert(rr.registers == [20]*8) # test the expected value
await asyncio.sleep(1)
def run_with_not_running_loop():
"""
A loop is created and is passed to ModbusClient factory to be used.
:return:
"""
log.debug("Running Async client with asyncio loop not yet started")
log.debug("------------------------------------------------------")
loop = asyncio.new_event_loop()
assert not loop.is_running()
new_loop, client = ModbusClient(schedulers.ASYNC_IO, port=5020, loop=loop)
loop.run_until_complete(start_async_test(client.protocol))
loop.close()
log.debug("--------------RUN_WITH_NOT_RUNNING_LOOP---------------")
log.debug("")
def run_with_already_running_loop():
"""
An already running loop is passed to ModbusClient Factory
:return:
"""
log.debug("Running Async client with asyncio loop already started")
log.debug("------------------------------------------------------")
def done(future):
log.info("Done !!!")
def start_loop(loop):
"""
Start Loop
:param loop:
:return:
"""
asyncio.set_event_loop(loop)
loop.run_forever()
loop = asyncio.new_event_loop()
t = Thread(target=start_loop, args=[loop])
t.daemon = True
# Start the loop
t.start()
assert loop.is_running()
asyncio.set_event_loop(loop)
loop, client = ModbusClient(schedulers.ASYNC_IO, port=5020, loop=loop)
future = asyncio.run_coroutine_threadsafe(
start_async_test(client.protocol), loop=loop)
future.add_done_callback(done)
while not future.done():
time.sleep(0.1)
loop.stop()
log.debug("--------DONE RUN_WITH_ALREADY_RUNNING_LOOP-------------")
log.debug("")
def run_with_no_loop():
"""
ModbusClient Factory creates a loop.
:return:
"""
loop, client = ModbusClient(schedulers.ASYNC_IO, port=5020)
loop.run_until_complete(start_async_test(client.protocol))
loop.close()
if __name__ == '__main__':
# Run with No loop
log.debug("Running Async client")
log.debug("------------------------------------------------------")
run_with_no_loop()
# Run with loop not yet started
run_with_not_running_loop()
# Run with already running loop
run_with_already_running_loop()
log.debug("---------------------RUN_WITH_NO_LOOP-----------------")
log.debug("")
pymodbus-2.1.0/examples/common/async_asyncio_serial_client.py 0000775 0000000 0000000 00000014672 13355134677 0024626 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Asynchronous Client Examples
--------------------------------------------------------------------------
The following is an example of how to use the asynchronous serial modbus
client implementation from pymodbus with ayncio.
The example is only valid on Python3.4 and above
"""
from pymodbus.compat import IS_PYTHON3, PYTHON_VERSION
if IS_PYTHON3 and PYTHON_VERSION >= (3, 4):
import logging
import asyncio
from pymodbus.client.async.serial import (
AsyncModbusSerialClient as ModbusClient)
from pymodbus.client.async import schedulers
else:
import sys
sys.stderr("This example needs to be run only on python 3.4 and above")
sys.exit(1)
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# specify slave to query
# --------------------------------------------------------------------------- #
# The slave to query is specified in an optional parameter for each
# individual request. This can be done by specifying the `unit` parameter
# which defaults to `0x00`
# --------------------------------------------------------------------------- #
UNIT = 0x01
async def start_async_test(client):
# ----------------------------------------------------------------------- #
# specify slave to query
# ----------------------------------------------------------------------- #
# The slave to query is specified in an optional parameter for each
# individual request. This can be done by specifying the `unit` parameter
# which defaults to `0x00`
# ----------------------------------------------------------------------- #
try:
log.debug("Reading Coils")
rr = client.read_coils(1, 1, unit=UNIT)
# ----------------------------------------------------------------------- #
# example requests
# ----------------------------------------------------------------------- #
# simply call the methods that you would like to use.
# An example session is displayed below along with some assert checks.
# Note that some modbus implementations differentiate holding/
# input discrete/coils and as such you will not be able to write to
# these, therefore the starting values are not known to these tests.
# Furthermore, some use the same memory blocks for the two sets,
# so a change to one is a change to the other.
# Keep both of these cases in mind when testing as the following will
# _only_ pass with the supplied async modbus server (script supplied).
# ----------------------------------------------------------------------- #
log.debug("Write to a Coil and read back")
rq = await client.write_coil(0, True, unit=UNIT)
rr = await client.read_coils(0, 1, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.bits[0] == True) # test the expected value
log.debug("Write to multiple coils and read back- test 1")
rq = await client.write_coils(1, [True]*8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
rr = await client.read_coils(1, 21, unit=UNIT)
assert(rr.function_code < 0x80) # test that we are not an error
resp = [True]*21
# If the returned output quantity is not a multiple of eight,
# the remaining bits in the final data byte will be padded with zeros
# (toward the high order end of the byte).
resp.extend([False]*3)
assert(rr.bits == resp) # test the expected value
log.debug("Write to multiple coils and read back - test 2")
rq = await client.write_coils(1, [False]*8, unit=UNIT)
rr = await client.read_coils(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.bits == [False]*8) # test the expected value
log.debug("Read discrete inputs")
rr = await client.read_discrete_inputs(0, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
log.debug("Write to a holding register and read back")
rq = await client.write_register(1, 10, unit=UNIT)
rr = await client.read_holding_registers(1, 1, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.registers[0] == 10) # test the expected value
log.debug("Write to multiple holding registers and read back")
rq = await client.write_registers(1, [10]*8, unit=UNIT)
rr = await client.read_holding_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.registers == [10]*8) # test the expected value
log.debug("Read input registers")
rr = await client.read_input_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
log.debug("Read write registers simulataneously")
rq = await client.readwrite_registers(unit=UNIT, **arguments)
rr = await client.read_holding_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rq.registers == [20]*8) # test the expected value
assert(rr.registers == [20]*8) # test the expected value
except Exception as e:
log.exception(e)
client.transport.close()
await asyncio.sleep(1)
if __name__ == '__main__':
# ----------------------------------------------------------------------- #
# For testing on linux based systems you can use socat to create serial
# ports
# ----------------------------------------------------------------------- #
# socat -d -d PTY,link=/tmp/ptyp0,raw,echo=0,ispeed=9600 PTY,
# link=/tmp/ttyp0,raw,echo=0,ospeed=9600
loop, client = ModbusClient(schedulers.ASYNC_IO, port='/tmp/ptyp0',
baudrate=9600, timeout=2, method="rtu")
loop.run_until_complete(start_async_test(client.protocol))
loop.close()
pymodbus-2.1.0/examples/common/async_tornado_client.py 0000775 0000000 0000000 00000012317 13355134677 0023262 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Asynchronous Client Examples
--------------------------------------------------------------------------
The following is an example of how to use the asynchronous modbus
client implementation from pymodbus using Tornado.
"""
import functools
from tornado.ioloop import IOLoop
from pymodbus.client.async import schedulers
# ---------------------------------------------------------------------------#
# choose the requested modbus protocol
# ---------------------------------------------------------------------------#
# from pymodbus.client.async.udp import AsyncModbusUDPClient as ModbusClient
from pymodbus.client.async.tcp import AsyncModbusTCPClient as ModbusClient
# ---------------------------------------------------------------------------#
# configure the client logging
# ---------------------------------------------------------------------------#
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# ---------------------------------------------------------------------------#
# helper method to test deferred callbacks
# ---------------------------------------------------------------------------#
def dassert(future, callback):
def _assertor(value):
# by pass assertion, an error here stops the write callbacks
assert value
def on_done(f):
exc = f.exception()
if exc:
log.debug(exc)
return _assertor(False)
return _assertor(callback(f.result()))
future.add_done_callback(on_done)
def _print(value):
if hasattr(value, "bits"):
t = value.bits
elif hasattr(value, "registers"):
t = value.registers
else:
log.error(value)
return
log.info("Printing : -- {}".format(t))
return t
UNIT = 0x01
# ---------------------------------------------------------------------------#
# example requests
# ---------------------------------------------------------------------------#
# simply call the methods that you would like to use. An example session
# is displayed below along with some assert checks. Note that unlike the
# synchronous version of the client, the asynchronous version returns
# deferreds which can be thought of as a handle to the callback to send
# the result of the operation. We are handling the result using the
# deferred assert helper(dassert).
# ---------------------------------------------------------------------------#
def beginAsynchronousTest(client, protocol):
rq = client.write_coil(1, True, unit=UNIT)
rr = client.read_coils(1, 1, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
rq = client.write_coils(1, [False]*8, unit=UNIT)
rr = client.read_coils(1, 8, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
rq = client.write_coils(1, [False]*8, unit=UNIT)
rr = client.read_discrete_inputs(1, 8, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
rq = client.write_register(1, 10, unit=UNIT)
rr = client.read_holding_registers(1, 1, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
rq = client.write_registers(1, [10]*8, unit=UNIT)
rr = client.read_input_registers(1, 8, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
rq = client.readwrite_registers(**arguments, unit=UNIT)
rr = client.read_input_registers(1,8, unit=UNIT)
dassert(rq, lambda r: r.registers == [20]*8) # test the expected value
dassert(rr, _print) # test the expected value
# -----------------------------------------------------------------------#
# close the client at some time later
# -----------------------------------------------------------------------#
IOLoop.current().add_timeout(IOLoop.current().time() + 1, client.close)
IOLoop.current().add_timeout(IOLoop.current().time() + 2, protocol.stop)
# ---------------------------------------------------------------------------#
# choose the client you want
# ---------------------------------------------------------------------------#
# make sure to start an implementation to hit against. For this
# you can use an existing device, the reference implementation in the tools
# directory, or start a pymodbus server.
# ---------------------------------------------------------------------------#
def err(*args, **kwargs):
log.error("Err", args, kwargs)
def callback(protocol, future):
log.debug("Client connected")
exp = future.exception()
if exp:
return err(exp)
client = future.result()
return beginAsynchronousTest(client, protocol)
if __name__ == "__main__":
protocol, future = ModbusClient(schedulers.IO_LOOP, port=5020)
future.add_done_callback(functools.partial(callback, protocol))
pymodbus-2.1.0/examples/common/async_tornado_client_serial.py 0000775 0000000 0000000 00000015521 13355134677 0024621 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Asynchronous Client Examples
--------------------------------------------------------------------------
The following is an example of how to use the asynchronous serial modbus
client implementation from pymodbus using tornado.
"""
# ---------------------------------------------------------------------------#
# import needed libraries
# ---------------------------------------------------------------------------#
import functools
from tornado.ioloop import IOLoop
from pymodbus.client.async import schedulers
# ---------------------------------------------------------------------------#
# choose the requested modbus protocol
# ---------------------------------------------------------------------------#
from pymodbus.client.async.serial import AsyncModbusSerialClient
# ---------------------------------------------------------------------------#
# configure the client logging
# ---------------------------------------------------------------------------#
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# ---------------------------------------------------------------------------#
# helper method to test deferred callbacks
# ---------------------------------------------------------------------------#
def dassert(future, callback):
def _assertor(value):
# by pass assertion, an error here stops the write callbacks
assert value
def on_done(f):
exc = f.exception()
if exc:
log.debug(exc)
return _assertor(False)
return _assertor(callback(f.result()))
future.add_done_callback(on_done)
def _print(value):
if hasattr(value, "bits"):
t = value.bits
elif hasattr(value, "registers"):
t = value.registers
else:
log.error(value)
return
log.info("Printing : -- {}".format(t))
return t
# ---------------------------------------------------------------------------#
# example requests
# ---------------------------------------------------------------------------#
# simply call the methods that you would like to use. An example session
# is displayed below along with some assert checks. Note that unlike the
# synchronous version of the client, the asynchronous version returns
# deferreds which can be thought of as a handle to the callback to send
# the result of the operation. We are handling the result using the
# deferred assert helper(dassert).
# ---------------------------------------------------------------------------#
UNIT = 0x01
def beginAsynchronousTest(client, protocol):
rq = client.write_coil(1, True, unit=UNIT)
rr = client.read_coils(1, 1, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
rq = client.write_coils(1, [False]*8, unit=UNIT)
rr = client.read_coils(1, 8, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
rq = client.write_coils(1, [False]*8, unit=UNIT)
rr = client.read_discrete_inputs(1, 8, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
rq = client.write_register(1, 10, unit=UNIT)
rr = client.read_holding_registers(1, 1, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
rq = client.write_registers(1, [10]*8, unit=UNIT)
rr = client.read_input_registers(1, 8, unit=UNIT)
dassert(rq, lambda r: r.function_code < 0x80) # test for no error
dassert(rr, _print) # test the expected value
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
rq = client.readwrite_registers(**arguments, unit=UNIT)
rr = client.read_input_registers(1,8, unit=UNIT)
dassert(rq, lambda r: r.registers == [20]*8) # test the expected value
dassert(rr, _print) # test the expected value
# -----------------------------------------------------------------------#
# close the client at some time later
# -----------------------------------------------------------------------#
IOLoop.current().add_timeout(IOLoop.current().time() + 1, client.close)
IOLoop.current().add_timeout(IOLoop.current().time() + 2, protocol.stop)
# ---------------------------------------------------------------------------#
# choose the client you want
# ---------------------------------------------------------------------------#
# make sure to start an implementation to hit against. For this
# you can use an existing device, the reference implementation in the tools
# directory, or start a pymodbus server.
# ---------------------------------------------------------------------------#
def err(*args, **kwargs):
log.error("Err", args, kwargs)
def callback(protocol, future):
log.debug("Client connected")
exp = future.exception()
if exp:
return err(exp)
client = future.result()
return beginAsynchronousTest(client, protocol)
if __name__ == "__main__":
# ----------------------------------------------------------------------- #
# Create temporary serial ports using SOCAT
# socat -d -d PTY,link=/tmp/ptyp0,raw,echo=0,ispeed=9600 PTY,
# link=/tmp/ttyp0,raw,echo=0,ospeed=9600
# Default framer is ModbusRtuFramer
# ----------------------------------------------------------------------- #
# Rtu
protocol, future = AsyncModbusSerialClient(schedulers.IO_LOOP,
method="rtu",
port="/dev/ptyp0",
baudrate=9600,
timeout=2)
# Asci
# from pymodbus.transaction import ModbusAsciiFramer
# protocol, future = AsyncModbusSerialClient(schedulers.IO_LOOP,
# method="ascii",
# port="/dev/ptyp0",
# framer=ModbusAsciiFramer,
# baudrate=9600,
# timeout=2)
# Binary
# from pymodbus.transaction import ModbusBinaryFramer
# protocol, future = AsyncModbusSerialClient(schedulers.IO_LOOP,
# method="binary",
# port="/dev/ptyp0",
# framer=ModbusBinaryFramer,
# baudrate=9600,
# timeout=2)
future.add_done_callback(functools.partial(callback, protocol))
pymodbus-2.1.0/examples/common/async_twisted_client.py 0000775 0000000 0000000 00000016022 13355134677 0023274 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Asynchronous Client Examples
--------------------------------------------------------------------------
The following is an example of how to use the asynchronous modbus
client implementation from pymodbus.
"""
# --------------------------------------------------------------------------- #
# import needed libraries
# --------------------------------------------------------------------------- #
from twisted.internet import reactor
from pymodbus.client.async.tcp import AsyncModbusTCPClient
# from pymodbus.client.async.udp import AsyncModbusUDPClient
from pymodbus.client.async import schedulers
# --------------------------------------------------------------------------- #
# choose the requested modbus protocol
# --------------------------------------------------------------------------- #
from twisted.internet import reactor, protocol
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# helper method to test deferred callbacks
# --------------------------------------------------------------------------- #
def err(*args, **kwargs):
logging.error("Err-{}-{}".format(args, kwargs))
def dassert(deferred, callback):
def _assertor(value):
assert value
deferred.addCallback(lambda r: _assertor(callback(r)))
deferred.addErrback(err)
# --------------------------------------------------------------------------- #
# specify slave to query
# --------------------------------------------------------------------------- #
# The slave to query is specified in an optional parameter for each
# individual request. This can be done by specifying the `unit` parameter
# which defaults to `0x00`
# --------------------------------------------------------------------------- #
UNIT = 0x01
def processResponse(result):
log.debug(result)
def exampleRequests(client):
rr = client.read_coils(1, 1, unit=0x02)
rr.addCallback(processResponse)
rr = client.read_holding_registers(1, 1, unit=0x02)
rr.addCallback(processResponse)
rr = client.read_discrete_inputs(1, 1, unit=0x02)
rr.addCallback(processResponse)
rr = client.read_input_registers(1, 1, unit=0x02)
rr.addCallback(processResponse)
stopAsynchronousTest(client)
# --------------------------------------------------------------------------- #
# example requests
# --------------------------------------------------------------------------- #
# simply call the methods that you would like to use. An example session
# is displayed below along with some assert checks. Note that unlike the
# synchronous version of the client, the asynchronous version returns
# deferreds which can be thought of as a handle to the callback to send
# the result of the operation. We are handling the result using the
# deferred assert helper(dassert).
# --------------------------------------------------------------------------- #
def stopAsynchronousTest(client):
# ----------------------------------------------------------------------- #
# close the client at some time later
# ----------------------------------------------------------------------- #
reactor.callLater(1, client.transport.loseConnection)
reactor.callLater(2, reactor.stop)
def beginAsynchronousTest(client):
rq = client.write_coil(1, True, unit=UNIT)
rr = client.read_coils(1, 1, unit=UNIT)
dassert(rq, lambda r: not r.isError()) # test for no error
dassert(rr, lambda r: r.bits[0] == True) # test the expected value
rq = client.write_coils(1, [True]*8, unit=UNIT)
rr = client.read_coils(1, 8, unit=UNIT)
dassert(rq, lambda r: not r.isError()) # test for no error
dassert(rr, lambda r: r.bits == [True]*8) # test the expected value
rq = client.write_coils(1, [False]*8, unit=UNIT)
rr = client.read_discrete_inputs(1, 8, unit=UNIT)
dassert(rq, lambda r: not r.isError()) # test for no error
dassert(rr, lambda r: r.bits == [True]*8) # test the expected value
rq = client.write_register(1, 10, unit=UNIT)
rr = client.read_holding_registers(1, 1, unit=UNIT)
dassert(rq, lambda r: not r.isError()) # test for no error
dassert(rr, lambda r: r.registers[0] == 10) # test the expected value
rq = client.write_registers(1, [10]*8, unit=UNIT)
rr = client.read_input_registers(1, 8, unit=UNIT)
dassert(rq, lambda r: not r.isError()) # test for no error
dassert(rr, lambda r: r.registers == [17]*8) # test the expected value
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
rq = client.readwrite_registers(arguments, unit=UNIT)
rr = client.read_input_registers(1, 8, unit=UNIT)
dassert(rq, lambda r: r.registers == [20]*8) # test the expected value
dassert(rr, lambda r: r.registers == [17]*8) # test the expected value
stopAsynchronousTest(client)
# ----------------------------------------------------------------------- #
# close the client at some time later
# ----------------------------------------------------------------------- #
# reactor.callLater(1, client.transport.loseConnection)
reactor.callLater(2, reactor.stop)
# --------------------------------------------------------------------------- #
# extra requests
# --------------------------------------------------------------------------- #
# If you are performing a request that is not available in the client
# mixin, you have to perform the request like this instead::
#
# from pymodbus.diag_message import ClearCountersRequest
# from pymodbus.diag_message import ClearCountersResponse
#
# request = ClearCountersRequest()
# response = client.execute(request)
# if isinstance(response, ClearCountersResponse):
# ... do something with the response
#
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
# choose the client you want
# --------------------------------------------------------------------------- #
# make sure to start an implementation to hit against. For this
# you can use an existing device, the reference implementation in the tools
# directory, or start a pymodbus server.
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
protocol, deferred = AsyncModbusTCPClient(schedulers.REACTOR, port=5020)
# protocol, deferred = AsyncModbusUDPClient(schedulers.REACTOR, port=5020)
# callback=beginAsynchronousTest,
# errback=err)
deferred.addCallback(beginAsynchronousTest)
deferred.addErrback(err)
pymodbus-2.1.0/examples/common/async_twisted_client_serial.py 0000775 0000000 0000000 00000005423 13355134677 0024636 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Asynchronous Client Examples
--------------------------------------------------------------------------
The following is an example of how to use the asynchronous serial modbus
client implementation from pymodbus with twisted.
"""
from twisted.internet import reactor
from pymodbus.client.async import schedulers
from pymodbus.client.async.serial import AsyncModbusSerialClient
from pymodbus.client.async.twisted import ModbusClientProtocol
import logging
logging.basicConfig()
log = logging.getLogger("pymodbus")
log.setLevel(logging.DEBUG)
# ---------------------------------------------------------------------------#
# state a few constants
# ---------------------------------------------------------------------------#
SERIAL_PORT = "/dev/ptyp0"
STATUS_REGS = (1, 2)
STATUS_COILS = (1, 3)
CLIENT_DELAY = 1
UNIT = 0x01
class ExampleProtocol(ModbusClientProtocol):
def __init__(self, framer):
""" Initializes our custom protocol
:param framer: The decoder to use to process messages
:param endpoint: The endpoint to send results to
"""
ModbusClientProtocol.__init__(self, framer)
log.debug("Beginning the processing loop")
reactor.callLater(CLIENT_DELAY, self.fetch_holding_registers)
def fetch_holding_registers(self):
""" Defer fetching holding registers
"""
log.debug("Starting the next cycle")
d = self.read_holding_registers(*STATUS_REGS, unit=UNIT)
d.addCallbacks(self.send_holding_registers, self.error_handler)
def send_holding_registers(self, response):
""" Write values of holding registers, defer fetching coils
:param response: The response to process
"""
log.info(response.getRegister(0))
log.info(response.getRegister(1))
d = self.read_coils(*STATUS_COILS, unit=UNIT)
d.addCallbacks(self.start_next_cycle, self.error_handler)
def start_next_cycle(self, response):
""" Write values of coils, trigger next cycle
:param response: The response to process
"""
log.info(response.getBit(0))
log.info(response.getBit(1))
log.info(response.getBit(2))
reactor.callLater(CLIENT_DELAY, self.fetch_holding_registers)
def error_handler(self, failure):
""" Handle any twisted errors
:param failure: The error to handle
"""
log.error(failure)
if __name__ == "__main__":
proto, client = AsyncModbusSerialClient(schedulers.REACTOR,
method="rtu",
port=SERIAL_PORT,
timeout=2,
proto_cls=ExampleProtocol)
proto.start()
# proto.stop()
pymodbus-2.1.0/examples/common/asynchronous_processor.py 0000775 0000000 0000000 00000016417 13355134677 0023720 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Asynchronous Processor Example
--------------------------------------------------------------------------
The following is a full example of a continuous client processor. Feel
free to use it as a skeleton guide in implementing your own.
"""
# --------------------------------------------------------------------------- #
# import the neccessary modules
# --------------------------------------------------------------------------- #
from twisted.internet import serialport, reactor
from twisted.internet.protocol import ClientFactory
from pymodbus.factory import ClientDecoder
from pymodbus.client.async.twisted import ModbusClientProtocol
# --------------------------------------------------------------------------- #
# Choose the framer you want to use
# --------------------------------------------------------------------------- #
# from pymodbus.transaction import ModbusBinaryFramer as ModbusFramer
# from pymodbus.transaction import ModbusAsciiFramer as ModbusFramer
from pymodbus.transaction import ModbusRtuFramer as ModbusFramer
# from pymodbus.transaction import ModbusSocketFramer as ModbusFramer
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# state a few constants
# --------------------------------------------------------------------------- #
SERIAL_PORT = "/dev/ptyp0"
STATUS_REGS = (1, 2)
STATUS_COILS = (1, 3)
CLIENT_DELAY = 1
UNIT = 0x01
# --------------------------------------------------------------------------- #
# an example custom protocol
# --------------------------------------------------------------------------- #
# Here you can perform your main procesing loop utilizing defereds and timed
# callbacks.
# --------------------------------------------------------------------------- #
class ExampleProtocol(ModbusClientProtocol):
def __init__(self, framer, endpoint):
""" Initializes our custom protocol
:param framer: The decoder to use to process messages
:param endpoint: The endpoint to send results to
"""
ModbusClientProtocol.__init__(self, framer)
self.endpoint = endpoint
log.debug("Beginning the processing loop")
reactor.callLater(CLIENT_DELAY, self.fetch_holding_registers)
def fetch_holding_registers(self):
""" Defer fetching holding registers
"""
log.debug("Starting the next cycle")
d = self.read_holding_registers(*STATUS_REGS, unit=UNIT)
d.addCallbacks(self.send_holding_registers, self.error_handler)
def send_holding_registers(self, response):
""" Write values of holding registers, defer fetching coils
:param response: The response to process
"""
self.endpoint.write(response.getRegister(0))
self.endpoint.write(response.getRegister(1))
d = self.read_coils(*STATUS_COILS, unit=UNIT)
d.addCallbacks(self.start_next_cycle, self.error_handler)
def start_next_cycle(self, response):
""" Write values of coils, trigger next cycle
:param response: The response to process
"""
self.endpoint.write(response.getBit(0))
self.endpoint.write(response.getBit(1))
self.endpoint.write(response.getBit(2))
reactor.callLater(CLIENT_DELAY, self.fetch_holding_registers)
def error_handler(self, failure):
""" Handle any twisted errors
:param failure: The error to handle
"""
log.error(failure)
# --------------------------------------------------------------------------- #
# a factory for the example protocol
# --------------------------------------------------------------------------- #
# This is used to build client protocol's if you tie into twisted's method
# of processing. It basically produces client instances of the underlying
# protocol::
#
# Factory(Protocol) -> ProtocolInstance
#
# It also persists data between client instances (think protocol singelton).
# --------------------------------------------------------------------------- #
class ExampleFactory(ClientFactory):
protocol = ExampleProtocol
def __init__(self, framer, endpoint):
""" Remember things necessary for building a protocols """
self.framer = framer
self.endpoint = endpoint
def buildProtocol(self, _):
""" Create a protocol and start the reading cycle """
proto = self.protocol(self.framer, self.endpoint)
proto.factory = self
return proto
# --------------------------------------------------------------------------- #
# a custom client for our device
# --------------------------------------------------------------------------- #
# Twisted provides a number of helper methods for creating and starting
# clients:
# - protocol.ClientCreator
# - reactor.connectTCP
#
# How you start your client is really up to you.
# --------------------------------------------------------------------------- #
class SerialModbusClient(serialport.SerialPort):
def __init__(self, factory, *args, **kwargs):
""" Setup the client and start listening on the serial port
:param factory: The factory to build clients with
"""
protocol = factory.buildProtocol(None)
self.decoder = ClientDecoder()
serialport.SerialPort.__init__(self, protocol, *args, **kwargs)
# --------------------------------------------------------------------------- #
# a custom endpoint for our results
# --------------------------------------------------------------------------- #
# An example line reader, this can replace with:
# - the TCP protocol
# - a context recorder
# - a database or file recorder
# --------------------------------------------------------------------------- #
class LoggingLineReader(object):
def write(self, response):
""" Handle the next modbus response
:param response: The response to process
"""
log.info("Read Data: %d" % response)
# --------------------------------------------------------------------------- #
# start running the processor
# --------------------------------------------------------------------------- #
# This initializes the client, the framer, the factory, and starts the
# twisted event loop (the reactor). It should be noted that a number of
# things could be chanegd as one sees fit:
# - The ModbusRtuFramer could be replaced with a ModbusAsciiFramer
# - The SerialModbusClient could be replaced with reactor.connectTCP
# - The LineReader endpoint could be replaced with a database store
# --------------------------------------------------------------------------- #
def main():
log.debug("Initializing the client")
framer = ModbusFramer(ClientDecoder(), client=None)
reader = LoggingLineReader()
factory = ExampleFactory(framer, reader)
SerialModbusClient(factory, SERIAL_PORT, reactor)
# factory = reactor.connectTCP("localhost", 502, factory)
log.debug("Starting the client")
reactor.run()
if __name__ == "__main__":
main()
pymodbus-2.1.0/examples/common/asynchronous_server.py 0000775 0000000 0000000 00000014446 13355134677 0023207 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Asynchronous Server Example
--------------------------------------------------------------------------
The asynchronous server is a high performance implementation using the
twisted library as its backend. This allows it to scale to many thousands
of nodes which can be helpful for testing monitoring software.
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.server.async import StartTcpServer
from pymodbus.server.async import StartUdpServer
from pymodbus.server.async import StartSerialServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import (ModbusRtuFramer,
ModbusAsciiFramer,
ModbusBinaryFramer)
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_async_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
# The datastores only respond to the addresses that they are initialized to
# Therefore, if you initialize a DataBlock to addresses from 0x00 to 0xFF,
# a request to 0x100 will respond with an invalid address exception.
# This is because many devices exhibit this kind of behavior (but not all)
#
# block = ModbusSequentialDataBlock(0x00, [0]*0xff)
#
# Continuing, you can choose to use a sequential or a sparse DataBlock in
# your data context. The difference is that the sequential has no gaps in
# the data while the sparse can. Once again, there are devices that exhibit
# both forms of behavior::
#
# block = ModbusSparseDataBlock({0x00: 0, 0x05: 1})
# block = ModbusSequentialDataBlock(0x00, [0]*5)
#
# Alternately, you can use the factory methods to initialize the DataBlocks
# or simply do not pass them to have them initialized to 0x00 on the full
# address range::
#
# store = ModbusSlaveContext(di = ModbusSequentialDataBlock.create())
# store = ModbusSlaveContext()
#
# Finally, you are allowed to use the same DataBlock reference for every
# table or you you may use a seperate DataBlock for each table.
# This depends if you would like functions to be able to access and modify
# the same data or not::
#
# block = ModbusSequentialDataBlock(0x00, [0]*0xff)
# store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
#
# The server then makes use of a server context that allows the server to
# respond with different slave contexts for different unit ids. By default
# it will return the same context for every unit id supplied (broadcast
# mode).
# However, this can be overloaded by setting the single flag to False
# and then supplying a dictionary of unit id to context mapping::
#
# slaves = {
# 0x01: ModbusSlaveContext(...),
# 0x02: ModbusSlaveContext(...),
# 0x03: ModbusSlaveContext(...),
# }
# context = ModbusServerContext(slaves=slaves, single=False)
#
# The slave context can also be initialized in zero_mode which means that a
# request to address(0-7) will map to the address (0-7). The default is
# False which is based on section 4.4 of the specification, so address(0-7)
# will map to (1-8)::
#
# store = ModbusSlaveContext(..., zero_mode=True)
# ----------------------------------------------------------------------- #
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [17]*100),
co=ModbusSequentialDataBlock(0, [17]*100),
hr=ModbusSequentialDataBlock(0, [17]*100),
ir=ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
# If you don't set this or any fields, they are defaulted to empty strings.
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '1.5'
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
# TCP Server
StartTcpServer(context, identity=identity, address=("localhost", 5020))
# TCP Server with deferred reactor run
# from twisted.internet import reactor
# StartTcpServer(context, identity=identity, address=("localhost", 5020),
# defer_reactor_run=True)
# reactor.run()
# Server with RTU framer
# StartTcpServer(context, identity=identity, address=("localhost", 5020),
# framer=ModbusRtuFramer)
# UDP Server
# StartUdpServer(context, identity=identity, address=("127.0.0.1", 5020))
# RTU Server
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', framer=ModbusRtuFramer)
# ASCII Server
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', framer=ModbusAsciiFramer)
# Binary Server
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', framer=ModbusBinaryFramer)
if __name__ == "__main__":
run_async_server()
pymodbus-2.1.0/examples/common/callback_server.py 0000775 0000000 0000000 00000012174 13355134677 0022204 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Server With Callbacks
--------------------------------------------------------------------------
This is an example of adding callbacks to a running modbus server
when a value is written to it. In order for this to work, it needs
a device-mapping file.
"""
# --------------------------------------------------------------------------- #
# import the modbus libraries we need
# --------------------------------------------------------------------------- #
from pymodbus.server.async import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
# --------------------------------------------------------------------------- #
# import the python libraries we need
# --------------------------------------------------------------------------- #
from multiprocessing import Queue, Process
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# create your custom data block with callbacks
# --------------------------------------------------------------------------- #
class CallbackDataBlock(ModbusSparseDataBlock):
""" A datablock that stores the new value in memory
and passes the operation to a message queue for further
processing.
"""
def __init__(self, devices, queue):
"""
"""
self.devices = devices
self.queue = queue
values = {k: 0 for k in devices.keys()}
values[0xbeef] = len(values) # the number of devices
super(CallbackDataBlock, self).__init__(values)
def setValues(self, address, value):
""" Sets the requested values of the datastore
:param address: The starting address
:param values: The new values to be set
"""
super(CallbackDataBlock, self).setValues(address, value)
self.queue.put((self.devices.get(address, None), value))
# --------------------------------------------------------------------------- #
# define your callback process
# --------------------------------------------------------------------------- #
def rescale_value(value):
""" Rescale the input value from the range
of 0..100 to -3200..3200.
:param value: The input value to scale
:returns: The rescaled value
"""
s = 1 if value >= 50 else -1
c = value if value < 50 else (value - 50)
return s * (c * 64)
def device_writer(queue):
""" A worker process that processes new messages
from a queue to write to device outputs
:param queue: The queue to get new messages from
"""
while True:
device, value = queue.get()
scaled = rescale_value(value[0])
log.debug("Write(%s) = %s" % (device, value))
if not device: continue
# do any logic here to update your devices
# --------------------------------------------------------------------------- #
# initialize your device map
# --------------------------------------------------------------------------- #
def read_device_map(path):
""" A helper method to read the device
path to address mapping from file::
0x0001,/dev/device1
0x0002,/dev/device2
:param path: The path to the input file
:returns: The input mapping file
"""
devices = {}
with open(path, 'r') as stream:
for line in stream:
piece = line.strip().split(',')
devices[int(piece[0], 16)] = piece[1]
return devices
def run_callback_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
queue = Queue()
devices = read_device_map("device-mapping")
block = CallbackDataBlock(devices, queue)
store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
p = Process(target=device_writer, args=(queue,))
p.start()
StartTcpServer(context, identity=identity, address=("localhost", 5020))
if __name__ == "__main__":
run_callback_server()
pymodbus-2.1.0/examples/common/changing_framers.py 0000775 0000000 0000000 00000005234 13355134677 0022356 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Client Framer Overload
--------------------------------------------------------------------------
All of the modbus clients are designed to have pluggable framers
so that the transport and protocol are decoupled. This allows a user
to define or plug in their custom protocols into existing transports
(like a binary framer over a serial connection).
It should be noted that although you are not limited to trying whatever
you would like, the library makes no gurantees that all framers with
all transports will produce predictable or correct results (for example
tcp transport with an RTU framer). However, please let us know of any
success cases that are not documented!
"""
# --------------------------------------------------------------------------- #
# import the modbus client and the framers
# --------------------------------------------------------------------------- #
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
# --------------------------------------------------------------------------- #
# Import the modbus framer that you want
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
from pymodbus.transaction import ModbusSocketFramer as ModbusFramer
# from pymodbus.transaction import ModbusRtuFramer as ModbusFramer
#from pymodbus.transaction import ModbusBinaryFramer as ModbusFramer
#from pymodbus.transaction import ModbusAsciiFramer as ModbusFramer
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
if __name__ == "__main__":
# ----------------------------------------------------------------------- #
# Initialize the client
# ----------------------------------------------------------------------- #
client = ModbusClient('localhost', port=5020, framer=ModbusFramer)
client.connect()
# ----------------------------------------------------------------------- #
# perform your requests
# ----------------------------------------------------------------------- #
rq = client.write_coil(1, True)
rr = client.read_coils(1,1)
assert(not rq.isError()) # test that we are not an error
assert(rr.bits[0] == True) # test the expected value
# ----------------------------------------------------------------------- #
# close the client
# ---------------------------------------------------------------------- #
client.close()
pymodbus-2.1.0/examples/common/custom_datablock.py 0000775 0000000 0000000 00000006352 13355134677 0022401 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Server With Custom Datablock Side Effect
--------------------------------------------------------------------------
This is an example of performing custom logic after a value has been
written to the datastore.
"""
# --------------------------------------------------------------------------- #
# import the modbus libraries we need
# --------------------------------------------------------------------------- #
from __future__ import print_function
from pymodbus.server.async import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# create your custom data block here
# --------------------------------------------------------------------------- #
class CustomDataBlock(ModbusSparseDataBlock):
""" A datablock that stores the new value in memory
and performs a custom action after it has been stored.
"""
def setValues(self, address, value):
""" Sets the requested values of the datastore
:param address: The starting address
:param values: The new values to be set
"""
super(ModbusSparseDataBlock, self).setValues(address, value)
# whatever you want to do with the written value is done here,
# however make sure not to do too much work here or it will
# block the server, espectially if the server is being written
# to very quickly
print("wrote {} to {}".format(value, address))
def run_custom_db_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
block = CustomDataBlock([0]*100)
store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
# p = Process(target=device_writer, args=(queue,))
# p.start()
StartTcpServer(context, identity=identity, address=("localhost", 5020))
if __name__ == "__main__":
run_custom_db_server()
pymodbus-2.1.0/examples/common/custom_message.py 0000775 0000000 0000000 00000007122 13355134677 0022075 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Synchronous Client Examples
--------------------------------------------------------------------------
The following is an example of how to use the synchronous modbus client
implementation from pymodbus.
It should be noted that the client can also be used with
the guard construct that is available in python 2.5 and up::
with ModbusClient('127.0.0.1') as client:
result = client.read_coils(1,10)
print result
"""
import struct
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.pdu import ModbusRequest, ModbusResponse, ModbusExceptions
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.bit_read_message import ReadCoilsRequest
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# create your custom message
# --------------------------------------------------------------------------- #
# The following is simply a read coil request that always reads 16 coils.
# Since the function code is already registered with the decoder factory,
# this will be decoded as a read coil response. If you implement a new
# method that is not currently implemented, you must register the request
# and response with a ClientDecoder factory.
# --------------------------------------------------------------------------- #
class CustomModbusResponse(ModbusResponse):
pass
class CustomModbusRequest(ModbusRequest):
function_code = 1
def __init__(self, address):
ModbusRequest.__init__(self)
self.address = address
self.count = 16
def encode(self):
return struct.pack('>HH', self.address, self.count)
def decode(self, data):
self.address, self.count = struct.unpack('>HH', data)
def execute(self, context):
if not (1 <= self.count <= 0x7d0):
return self.doException(ModbusExceptions.IllegalValue)
if not context.validate(self.function_code, self.address, self.count):
return self.doException(ModbusExceptions.IllegalAddress)
values = context.getValues(self.function_code, self.address,
self.count)
return CustomModbusResponse(values)
# --------------------------------------------------------------------------- #
# This could also have been defined as
# --------------------------------------------------------------------------- #
class Read16CoilsRequest(ReadCoilsRequest):
def __init__(self, address):
""" Initializes a new instance
:param address: The address to start reading from
"""
ReadCoilsRequest.__init__(self, address, 16)
# --------------------------------------------------------------------------- #
# execute the request with your client
# --------------------------------------------------------------------------- #
# using the with context, the client will automatically be connected
# and closed when it leaves the current scope.
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
with ModbusClient('127.0.0.1') as client:
request = CustomModbusRequest(0)
result = client.execute(request)
print(result)
pymodbus-2.1.0/examples/common/dbstore_update_server.py 0000664 0000000 0000000 00000010044 13355134677 0023443 0 ustar 00root root 0000000 0000000 """
Pymodbus Server With Updating Thread
--------------------------------------------------------------------------
This is an example of having a background thread updating the
context in an SQLite4 database while the server is operating.
This scrit generates a random address range (within 0 - 65000) and a random
value and stores it in a database. It then reads the same address to verify
that the process works as expected
This can also be done with a python thread::
from threading import Thread
thread = Thread(target=updating_writer, args=(context,))
thread.start()
"""
# --------------------------------------------------------------------------- #
# import the modbus libraries we need
# --------------------------------------------------------------------------- #
from pymodbus.server.async import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusServerContext
from pymodbus.datastore.database import SqlSlaveContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
import random
# --------------------------------------------------------------------------- #
# import the twisted libraries we need
# --------------------------------------------------------------------------- #
from twisted.internet.task import LoopingCall
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# define your callback process
# --------------------------------------------------------------------------- #
def updating_writer(a):
""" A worker process that runs every so often and
updates live values of the context which resides in an SQLite3 database.
It should be noted that there is a race condition for the update.
:param arguments: The input arguments to the call
"""
log.debug("Updating the database context")
context = a[0]
readfunction = 0x03 # read holding registers
writefunction = 0x10
slave_id = 0x01 # slave address
count = 50
# import pdb; pdb.set_trace()
rand_value = random.randint(0, 9999)
rand_addr = random.randint(0, 65000)
log.debug("Writing to datastore: {}, {}".format(rand_addr, rand_value))
# import pdb; pdb.set_trace()
context[slave_id].setValues(writefunction, rand_addr, [rand_value])
values = context[slave_id].getValues(readfunction, rand_addr, count)
log.debug("Values from datastore: " + str(values))
def run_dbstore_update_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
block = ModbusSequentialDataBlock(0x00, [0] * 0xff)
store = SqlSlaveContext(block)
context = ModbusServerContext(slaves={1: store}, single=False)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
time = 5 # 5 seconds delay
loop = LoopingCall(f=updating_writer, a=(context,))
loop.start(time, now=False) # initially delay by time
StartTcpServer(context, identity=identity, address=("", 5020))
if __name__ == "__main__":
run_dbstore_update_server()
pymodbus-2.1.0/examples/common/device_mapping 0000664 0000000 0000000 00000000043 13355134677 0021372 0 ustar 00root root 0000000 0000000 0x0001,/dev/ptyp0
0x0002,/dev/ptyp1 pymodbus-2.1.0/examples/common/modbus_logging.py 0000775 0000000 0000000 00000003777 13355134677 0022072 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Logging Examples
--------------------------------------------------------------------------
"""
import logging
import logging.handlers as Handlers
if __name__ == "__main__":
# ----------------------------------------------------------------------- #
# This will simply send everything logged to console
# ----------------------------------------------------------------------- #
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# ----------------------------------------------------------------------- #
# This will send the error messages in the specified namespace to a file.
# The available namespaces in pymodbus are as follows:
# ----------------------------------------------------------------------- #
# * pymodbus.* - The root namespace
# * pymodbus.server.* - all logging messages involving the modbus server
# * pymodbus.client.* - all logging messages involving the client
# * pymodbus.protocol.* - all logging messages inside the protocol layer
# ----------------------------------------------------------------------- #
logging.basicConfig()
log = logging.getLogger('pymodbus.server')
log.setLevel(logging.ERROR)
# ----------------------------------------------------------------------- #
# This will send the error messages to the specified handlers:
# * docs.python.org/library/logging.html
# ----------------------------------------------------------------------- #
log = logging.getLogger('pymodbus')
log.setLevel(logging.ERROR)
handlers = [
Handlers.RotatingFileHandler("logfile", maxBytes=1024*1024),
Handlers.SMTPHandler("mx.host.com",
"pymodbus@host.com",
["support@host.com"],
"Pymodbus"),
Handlers.SysLogHandler(facility="daemon"),
Handlers.DatagramHandler('localhost', 12345),
]
[log.addHandler(h) for h in handlers]
pymodbus-2.1.0/examples/common/modbus_payload.py 0000775 0000000 0000000 00000014103 13355134677 0022056 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Payload Building/Decoding Example
--------------------------------------------------------------------------
# Run modbus-payload-server.py or synchronous-server.py to check the behavior
"""
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.compat import iteritems
from collections import OrderedDict
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.INFO)
def run_binary_payload_ex():
# ----------------------------------------------------------------------- #
# We are going to use a simple client to send our requests
# ----------------------------------------------------------------------- #
client = ModbusClient('127.0.0.1', port=5020)
client.connect()
# ----------------------------------------------------------------------- #
# If you need to build a complex message to send, you can use the payload
# builder to simplify the packing logic.
#
# Here we demonstrate packing a random payload layout, unpacked it looks
# like the following:
#
# - a 8 byte string 'abcdefgh'
# - a 32 bit float 22.34
# - a 16 bit unsigned int 0x1234
# - another 16 bit unsigned int 0x5678
# - an 8 bit int 0x12
# - an 8 bit bitstring [0,1,0,1,1,0,1,0]
# - an 32 bit uint 0x12345678
# - an 32 bit signed int -0x1234
# - an 64 bit signed int 0x12345678
# The packing can also be applied to the word (wordorder) and bytes in each
# word (byteorder)
# The wordorder is applicable only for 32 and 64 bit values
# Lets say we need to write a value 0x12345678 to a 32 bit register
# The following combinations could be used to write the register
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
# Word Order - Big Byte Order - Big
# word1 =0x1234 word2 = 0x5678
# Word Order - Big Byte Order - Little
# word1 =0x3412 word2 = 0x7856
# Word Order - Little Byte Order - Big
# word1 = 0x5678 word2 = 0x1234
# Word Order - Little Byte Order - Little
# word1 =0x7856 word2 = 0x3412
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
# ----------------------------------------------------------------------- #
builder = BinaryPayloadBuilder(byteorder=Endian.Big,
wordorder=Endian.Little)
builder.add_string('abcdefgh')
builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0])
builder.add_8bit_int(-0x12)
builder.add_8bit_uint(0x12)
builder.add_16bit_int(-0x5678)
builder.add_16bit_uint(0x1234)
builder.add_32bit_int(-0x1234)
builder.add_32bit_uint(0x12345678)
builder.add_32bit_float(22.34)
builder.add_32bit_float(-22.34)
builder.add_64bit_int(-0xDEADBEEF)
builder.add_64bit_uint(0x12345678DEADBEEF)
builder.add_64bit_uint(0x12345678DEADBEEF)
builder.add_64bit_float(123.45)
builder.add_64bit_float(-123.45)
payload = builder.to_registers()
print("-" * 60)
print("Writing Registers")
print("-" * 60)
print(payload)
print("\n")
payload = builder.build()
address = 0
# Can write registers
# registers = builder.to_registers()
# client.write_registers(address, registers, unit=1)
# Or can write encoded binary string
client.write_registers(address, payload, skip_encode=True, unit=1)
# ----------------------------------------------------------------------- #
# If you need to decode a collection of registers in a weird layout, the
# payload decoder can help you as well.
#
# Here we demonstrate decoding a random register layout, unpacked it looks
# like the following:
#
# - a 8 byte string 'abcdefgh'
# - a 32 bit float 22.34
# - a 16 bit unsigned int 0x1234
# - another 16 bit unsigned int which we will ignore
# - an 8 bit int 0x12
# - an 8 bit bitstring [0,1,0,1,1,0,1,0]
# ----------------------------------------------------------------------- #
address = 0x0
count = len(payload)
result = client.read_holding_registers(address, count, unit=1)
print("-" * 60)
print("Registers")
print("-" * 60)
print(result.registers)
print("\n")
decoder = BinaryPayloadDecoder.fromRegisters(result.registers,
byteorder=Endian.Little,
wordorder=Endian.Little)
decoded = OrderedDict([
('string', decoder.decode_string(8)),
('bits', decoder.decode_bits()),
('8int', decoder.decode_8bit_int()),
('8uint', decoder.decode_8bit_uint()),
('16int', decoder.decode_16bit_int()),
('16uint', decoder.decode_16bit_uint()),
('32int', decoder.decode_32bit_int()),
('32uint', decoder.decode_32bit_uint()),
('32float', decoder.decode_32bit_float()),
('32float2', decoder.decode_32bit_float()),
('64int', decoder.decode_64bit_int()),
('64uint', decoder.decode_64bit_uint()),
('ignore', decoder.skip_bytes(8)),
('64float', decoder.decode_64bit_float()),
('64float2', decoder.decode_64bit_float()),
])
print("-" * 60)
print("Decoded Data")
print("-" * 60)
for name, value in iteritems(decoded):
print("%s\t" % name, hex(value) if isinstance(value, int) else value)
# ----------------------------------------------------------------------- #
# close the client
# ----------------------------------------------------------------------- #
client.close()
if __name__ == "__main__":
run_binary_payload_ex()
pymodbus-2.1.0/examples/common/modbus_payload_server.py 0000775 0000000 0000000 00000007465 13355134677 0023461 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Server Payload Example
--------------------------------------------------------------------------
If you want to initialize a server context with a complicated memory
layout, you can actually use the payload builder.
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.server.sync import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
# --------------------------------------------------------------------------- #
# import the payload builder
# --------------------------------------------------------------------------- #
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.payload import BinaryPayloadBuilder
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_payload_server():
# ----------------------------------------------------------------------- #
# build your payload
# ----------------------------------------------------------------------- #
builder = BinaryPayloadBuilder(byteorder=Endian.Little,
wordorder=Endian.Little)
builder.add_string('abcdefgh')
builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0])
builder.add_8bit_int(-0x12)
builder.add_8bit_uint(0x12)
builder.add_16bit_int(-0x5678)
builder.add_16bit_uint(0x1234)
builder.add_32bit_int(-0x1234)
builder.add_32bit_uint(0x12345678)
builder.add_32bit_float(22.34)
builder.add_32bit_float(-22.34)
builder.add_64bit_int(-0xDEADBEEF)
builder.add_64bit_uint(0x12345678DEADBEEF)
builder.add_64bit_uint(0xDEADBEEFDEADBEED)
builder.add_64bit_float(123.45)
builder.add_64bit_float(-123.45)
# ----------------------------------------------------------------------- #
# use that payload in the data store
# ----------------------------------------------------------------------- #
# Here we use the same reference block for each underlying store.
# ----------------------------------------------------------------------- #
block = ModbusSequentialDataBlock(1, builder.to_registers())
store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
# If you don't set this or any fields, they are defaulted to empty strings.
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '1.5'
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
StartTcpServer(context, identity=identity, address=("localhost", 5020))
if __name__ == "__main__":
run_payload_server()
pymodbus-2.1.0/examples/common/performance.py 0000775 0000000 0000000 00000007721 13355134677 0021365 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Performance Example
--------------------------------------------------------------------------
The following is an quick performance check of the synchronous
modbus client.
"""
# --------------------------------------------------------------------------- #
# import the necessary modules
# --------------------------------------------------------------------------- #
from __future__ import print_function
import logging, os
from time import time
# from pymodbus.client.sync import ModbusTcpClient
from pymodbus.client.sync import ModbusSerialClient
try:
from multiprocessing import log_to_stderr
except ImportError:
import logging
logging.basicConfig()
log_to_stderr = logging.getLogger
# --------------------------------------------------------------------------- #
# choose between threads or processes
# --------------------------------------------------------------------------- #
#from multiprocessing import Process as Worker
from threading import Thread as Worker
from threading import Lock
_thread_lock = Lock()
# --------------------------------------------------------------------------- #
# initialize the test
# --------------------------------------------------------------------------- #
# Modify the parameters below to control how we are testing the client:
#
# * workers - the number of workers to use at once
# * cycles - the total number of requests to send
# * host - the host to send the requests to
# --------------------------------------------------------------------------- #
workers = 10
cycles = 1000
host = '127.0.0.1'
# --------------------------------------------------------------------------- #
# perform the test
# --------------------------------------------------------------------------- #
# This test is written such that it can be used by many threads of processes
# although it should be noted that there are performance penalties
# associated with each strategy.
# --------------------------------------------------------------------------- #
def single_client_test(host, cycles):
""" Performs a single threaded test of a synchronous
client against the specified host
:param host: The host to connect to
:param cycles: The number of iterations to perform
"""
logger = log_to_stderr()
logger.setLevel(logging.DEBUG)
logger.debug("starting worker: %d" % os.getpid())
try:
count = 0
# client = ModbusTcpClient(host, port=5020)
client = ModbusSerialClient(method="rtu",
port="/dev/ttyp0", baudrate=9600)
while count < cycles:
with _thread_lock:
client.read_holding_registers(10, 1, unit=1).registers[0]
count += 1
except:
logger.exception("failed to run test successfully")
logger.debug("finished worker: %d" % os.getpid())
# --------------------------------------------------------------------------- #
# run our test and check results
# --------------------------------------------------------------------------- #
# We shard the total number of requests to perform between the number of
# threads that was specified. We then start all the threads and block on
# them to finish. This may need to switch to another mechanism to signal
# finished as the process/thread start up/shut down may skew the test a bit.
# RTU 32 requests/second @9600
# TCP 31430 requests/second
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
args = (host, int(cycles * 1.0 / workers))
procs = [Worker(target=single_client_test, args=args)
for _ in range(workers)]
start = time()
any(p.start() for p in procs) # start the workers
any(p.join() for p in procs) # wait for the workers to finish
stop = time()
print("%d requests/second" % ((1.0 * cycles) / (stop - start)))
print("time taken to complete %s cycle by "
"%s workers is %s seconds" % (cycles, workers, stop-start))
pymodbus-2.1.0/examples/common/synchronous_client.py 0000775 0000000 0000000 00000016403 13355134677 0023011 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Synchronous Client Examples
--------------------------------------------------------------------------
The following is an example of how to use the synchronous modbus client
implementation from pymodbus.
It should be noted that the client can also be used with
the guard construct that is available in python 2.5 and up::
with ModbusClient('127.0.0.1') as client:
result = client.read_coils(1,10)
print result
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
# from pymodbus.client.sync import ModbusTcpClient as ModbusClient
# from pymodbus.client.sync import ModbusUdpClient as ModbusClient
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
UNIT = 0x1
def run_sync_client():
# ------------------------------------------------------------------------#
# choose the client you want
# ------------------------------------------------------------------------#
# make sure to start an implementation to hit against. For this
# you can use an existing device, the reference implementation in the tools
# directory, or start a pymodbus server.
#
# If you use the UDP or TCP clients, you can override the framer being used
# to use a custom implementation (say RTU over TCP). By default they use
# the socket framer::
#
# client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer)
#
# It should be noted that you can supply an ipv4 or an ipv6 host address
# for both the UDP and TCP clients.
#
# There are also other options that can be set on the client that controls
# how transactions are performed. The current ones are:
#
# * retries - Specify how many retries to allow per transaction (default=3)
# * retry_on_empty - Is an empty response a retry (default = False)
# * source_address - Specifies the TCP source address to bind to
#
# Here is an example of using these options::
#
# client = ModbusClient('localhost', retries=3, retry_on_empty=True)
# ------------------------------------------------------------------------#
client = ModbusClient('localhost', port=5020)
# from pymodbus.transaction import ModbusRtuFramer
# client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer)
# client = ModbusClient(method='binary', port='/dev/ptyp0', timeout=1)
# client = ModbusClient(method='ascii', port='/dev/ptyp0', timeout=1)
# client = ModbusClient(method='rtu', port='/dev/ptyp0', timeout=1,
# baudrate=9600)
client.connect()
# ------------------------------------------------------------------------#
# specify slave to query
# ------------------------------------------------------------------------#
# The slave to query is specified in an optional parameter for each
# individual request. This can be done by specifying the `unit` parameter
# which defaults to `0x00`
# ----------------------------------------------------------------------- #
log.debug("Reading Coils")
rr = client.read_coils(1, 1, unit=UNIT)
log.debug(rr)
# ----------------------------------------------------------------------- #
# example requests
# ----------------------------------------------------------------------- #
# simply call the methods that you would like to use. An example session
# is displayed below along with some assert checks. Note that some modbus
# implementations differentiate holding/input discrete/coils and as such
# you will not be able to write to these, therefore the starting values
# are not known to these tests. Furthermore, some use the same memory
# blocks for the two sets, so a change to one is a change to the other.
# Keep both of these cases in mind when testing as the following will
# _only_ pass with the supplied async modbus server (script supplied).
# ----------------------------------------------------------------------- #
log.debug("Write to a Coil and read back")
rq = client.write_coil(0, True, unit=UNIT)
rr = client.read_coils(0, 1, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rr.bits[0] == True) # test the expected value
log.debug("Write to multiple coils and read back- test 1")
rq = client.write_coils(1, [True]*8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
rr = client.read_coils(1, 21, unit=UNIT)
assert(not rr.isError()) # test that we are not an error
resp = [True]*21
# If the returned output quantity is not a multiple of eight,
# the remaining bits in the final data byte will be padded with zeros
# (toward the high order end of the byte).
resp.extend([False]*3)
assert(rr.bits == resp) # test the expected value
log.debug("Write to multiple coils and read back - test 2")
rq = client.write_coils(1, [False]*8, unit=UNIT)
rr = client.read_coils(1, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rr.bits == [False]*8) # test the expected value
log.debug("Read discrete inputs")
rr = client.read_discrete_inputs(0, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
log.debug("Write to a holding register and read back")
rq = client.write_register(1, 10, unit=UNIT)
rr = client.read_holding_registers(1, 1, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rr.registers[0] == 10) # test the expected value
log.debug("Write to multiple holding registers and read back")
rq = client.write_registers(1, [10]*8, unit=UNIT)
rr = client.read_holding_registers(1, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rr.registers == [10]*8) # test the expected value
log.debug("Read input registers")
rr = client.read_input_registers(1, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
log.debug("Read write registeres simulataneously")
rq = client.readwrite_registers(unit=UNIT, **arguments)
rr = client.read_holding_registers(1, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rq.registers == [20]*8) # test the expected value
assert(rr.registers == [20]*8) # test the expected value
# ----------------------------------------------------------------------- #
# close the client
# ----------------------------------------------------------------------- #
client.close()
if __name__ == "__main__":
run_sync_client()
pymodbus-2.1.0/examples/common/synchronous_client_ext.py 0000775 0000000 0000000 00000023442 13355134677 0023672 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Synchronous Client Extended Examples
--------------------------------------------------------------------------
The following is an example of how to use the synchronous modbus client
implementation from pymodbus to perform the extended portions of the
modbus protocol.
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
# from pymodbus.client.sync import ModbusTcpClient as ModbusClient
# from pymodbus.client.sync import ModbusUdpClient as ModbusClient
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
# --------------------------------------------------------------------------- #
# import the extended messages to perform
# --------------------------------------------------------------------------- #
from pymodbus.diag_message import *
from pymodbus.file_message import *
from pymodbus.other_message import *
from pymodbus.mei_message import *
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
UNIT = 0x01
def execute_extended_requests():
# ------------------------------------------------------------------------#
# choose the client you want
# ------------------------------------------------------------------------#
# make sure to start an implementation to hit against. For this
# you can use an existing device, the reference implementation in the tools
# directory, or start a pymodbus server.
#
# It should be noted that you can supply an ipv4 or an ipv6 host address
# for both the UDP and TCP clients.
# ------------------------------------------------------------------------#
client = ModbusClient(method='rtu', port="/dev/ptyp0")
# client = ModbusClient(method='ascii', port="/dev/ptyp0")
# client = ModbusClient(method='binary', port="/dev/ptyp0")
# client = ModbusClient('127.0.0.1', port=5020)
# from pymodbus.transaction import ModbusRtuFramer
# client = ModbusClient('127.0.0.1', port=5020, framer=ModbusRtuFramer)
client.connect()
# ----------------------------------------------------------------------- #
# extra requests
# ----------------------------------------------------------------------- #
# If you are performing a request that is not available in the client
# mixin, you have to perform the request like this instead::
#
# from pymodbus.diag_message import ClearCountersRequest
# from pymodbus.diag_message import ClearCountersResponse
#
# request = ClearCountersRequest()
# response = client.execute(request)
# if isinstance(response, ClearCountersResponse):
# ... do something with the response
#
#
# What follows is a listing of all the supported methods. Feel free to
# comment, uncomment, or modify each result set to match with your ref.
# ----------------------------------------------------------------------- #
# ----------------------------------------------------------------------- #
# information requests
# ----------------------------------------------------------------------- #
log.debug("Running ReadDeviceInformationRequest")
rq = ReadDeviceInformationRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
# assert (not rr.isError()) # test that we are not an error
# assert (rr.information[0] == b'Pymodbus') # test the vendor name
# assert (rr.information[1] == b'PM') # test the product code
# assert (rr.information[2] == b'1.0') # test the code revision
log.debug("Running ReportSlaveIdRequest")
rq = ReportSlaveIdRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
# assert(not rr.isError()) # test that we are not an error
# assert(rr.identifier == 0x00) # test the slave identifier
# assert(rr.status == 0x00) # test that the status is ok
log.debug("Running ReadExceptionStatusRequest")
rq = ReadExceptionStatusRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
# assert(not rr.isError()) # test that we are not an error
# assert(rr.status == 0x55) # test the status code
log.debug("Running GetCommEventCounterRequest")
rq = GetCommEventCounterRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
# assert(not rr.isError()) # test that we are not an error
# assert(rr.status == True) # test the status code
# assert(rr.count == 0x00) # test the status code
log.debug("Running GetCommEventLogRequest")
rq = GetCommEventLogRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
# assert(not rr.isError()) # test that we are not an error
# assert(rr.status == True) # test the status code
# assert(rr.event_count == 0x00) # test the number of events
# assert(rr.message_count == 0x00) # test the number of messages
# assert(len(rr.events) == 0x00) # test the number of events
# ------------------------------------------------------------------------#
# diagnostic requests
# ------------------------------------------------------------------------#
log.debug("Running ReturnQueryDataRequest")
rq = ReturnQueryDataRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
# assert(rr.message[0] == 0x0000) # test the resulting message
log.debug("Running RestartCommunicationsOptionRequest")
rq = RestartCommunicationsOptionRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
# assert(rr.message == 0x0000) # test the resulting message
log.debug("Running ReturnDiagnosticRegisterRequest")
rq = ReturnDiagnosticRegisterRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ChangeAsciiInputDelimiterRequest")
rq = ChangeAsciiInputDelimiterRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ForceListenOnlyModeRequest")
rq = ForceListenOnlyModeRequest(unit=UNIT)
rr = client.execute(rq) # does not send a response
log.debug(rr)
log.debug("Running ClearCountersRequest")
rq = ClearCountersRequest()
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ReturnBusCommunicationErrorCountRequest")
rq = ReturnBusCommunicationErrorCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ReturnBusExceptionErrorCountRequest")
rq = ReturnBusExceptionErrorCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ReturnSlaveMessageCountRequest")
rq = ReturnSlaveMessageCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ReturnSlaveNoResponseCountRequest")
rq = ReturnSlaveNoResponseCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ReturnSlaveNAKCountRequest")
rq = ReturnSlaveNAKCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ReturnSlaveBusyCountRequest")
rq = ReturnSlaveBusyCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ReturnSlaveBusCharacterOverrunCountRequest")
rq = ReturnSlaveBusCharacterOverrunCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ReturnIopOverrunCountRequest")
rq = ReturnIopOverrunCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running ClearOverrunCountRequest")
rq = ClearOverrunCountRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
log.debug("Running GetClearModbusPlusRequest")
rq = GetClearModbusPlusRequest(unit=UNIT)
rr = client.execute(rq)
log.debug(rr)
# assert(rr == None) # not supported by reference
# ------------------------------------------------------------------------#
# close the client
# ------------------------------------------------------------------------#
client.close()
if __name__ == "__main__":
execute_extended_requests()
pymodbus-2.1.0/examples/common/synchronous_server.py 0000775 0000000 0000000 00000014147 13355134677 0023044 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Synchronous Server Example
--------------------------------------------------------------------------
The synchronous server is implemented in pure python without any third
party libraries (unless you need to use the serial protocols which require
pyserial). This is helpful in constrained or old environments where using
twisted is just not feasible. What follows is an example of its use:
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.server.sync import StartTcpServer
from pymodbus.server.sync import StartUdpServer
from pymodbus.server.sync import StartSerialServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusBinaryFramer
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
# The datastores only respond to the addresses that they are initialized to
# Therefore, if you initialize a DataBlock to addresses of 0x00 to 0xFF, a
# request to 0x100 will respond with an invalid address exception. This is
# because many devices exhibit this kind of behavior (but not all)::
#
# block = ModbusSequentialDataBlock(0x00, [0]*0xff)
#
# Continuing, you can choose to use a sequential or a sparse DataBlock in
# your data context. The difference is that the sequential has no gaps in
# the data while the sparse can. Once again, there are devices that exhibit
# both forms of behavior::
#
# block = ModbusSparseDataBlock({0x00: 0, 0x05: 1})
# block = ModbusSequentialDataBlock(0x00, [0]*5)
#
# Alternately, you can use the factory methods to initialize the DataBlocks
# or simply do not pass them to have them initialized to 0x00 on the full
# address range::
#
# store = ModbusSlaveContext(di = ModbusSequentialDataBlock.create())
# store = ModbusSlaveContext()
#
# Finally, you are allowed to use the same DataBlock reference for every
# table or you may use a separate DataBlock for each table.
# This depends if you would like functions to be able to access and modify
# the same data or not::
#
# block = ModbusSequentialDataBlock(0x00, [0]*0xff)
# store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
#
# The server then makes use of a server context that allows the server to
# respond with different slave contexts for different unit ids. By default
# it will return the same context for every unit id supplied (broadcast
# mode).
# However, this can be overloaded by setting the single flag to False and
# then supplying a dictionary of unit id to context mapping::
#
# slaves = {
# 0x01: ModbusSlaveContext(...),
# 0x02: ModbusSlaveContext(...),
# 0x03: ModbusSlaveContext(...),
# }
# context = ModbusServerContext(slaves=slaves, single=False)
#
# The slave context can also be initialized in zero_mode which means that a
# request to address(0-7) will map to the address (0-7). The default is
# False which is based on section 4.4 of the specification, so address(0-7)
# will map to (1-8)::
#
# store = ModbusSlaveContext(..., zero_mode=True)
# ----------------------------------------------------------------------- #
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [17]*100),
co=ModbusSequentialDataBlock(0, [17]*100),
hr=ModbusSequentialDataBlock(0, [17]*100),
ir=ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
# If you don't set this or any fields, they are defaulted to empty strings.
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '1.5'
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
# Tcp:
StartTcpServer(context, identity=identity, address=("localhost", 5020))
# TCP with different framer
# StartTcpServer(context, identity=identity,
# framer=ModbusRtuFramer, address=("0.0.0.0", 5020))
# Udp:
# StartUdpServer(context, identity=identity, address=("0.0.0.0", 5020))
# Ascii:
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', timeout=1)
# RTU:
# StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
# port='/dev/ttyp0', timeout=.005, baudrate=9600)
# Binary
# StartSerialServer(context,
# identity=identity,
# framer=ModbusBinaryFramer,
# port='/dev/ttyp0',
# timeout=1)
if __name__ == "__main__":
run_server()
pymodbus-2.1.0/examples/common/updating_server.py 0000775 0000000 0000000 00000007177 13355134677 0022272 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Server With Updating Thread
--------------------------------------------------------------------------
This is an example of having a background thread updating the
context while the server is operating. This can also be done with
a python thread::
from threading import Thread
thread = Thread(target=updating_writer, args=(context,))
thread.start()
"""
# --------------------------------------------------------------------------- #
# import the modbus libraries we need
# --------------------------------------------------------------------------- #
from pymodbus.server.async import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
# --------------------------------------------------------------------------- #
# import the twisted libraries we need
# --------------------------------------------------------------------------- #
from twisted.internet.task import LoopingCall
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# define your callback process
# --------------------------------------------------------------------------- #
def updating_writer(a):
""" A worker process that runs every so often and
updates live values of the context. It should be noted
that there is a race condition for the update.
:param arguments: The input arguments to the call
"""
log.debug("updating the context")
context = a[0]
register = 3
slave_id = 0x00
address = 0x10
values = context[slave_id].getValues(register, address, count=5)
values = [v + 1 for v in values]
log.debug("new values: " + str(values))
context[slave_id].setValues(register, address, values)
def run_updating_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [17]*100),
co=ModbusSequentialDataBlock(0, [17]*100),
hr=ModbusSequentialDataBlock(0, [17]*100),
ir=ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
time = 5 # 5 seconds delay
loop = LoopingCall(f=updating_writer, a=(context,))
loop.start(time, now=False) # initially delay by time
StartTcpServer(context, identity=identity, address=("localhost", 5020))
if __name__ == "__main__":
run_updating_server()
pymodbus-2.1.0/examples/contrib/ 0000775 0000000 0000000 00000000000 13355134677 0016650 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/examples/contrib/README.rst 0000664 0000000 0000000 00000003252 13355134677 0020341 0 ustar 00root root 0000000 0000000 ============================================================
Contributed Implementations
============================================================
There are a few example implementations of custom utilities
interacting with the pymodbus library just to show what is
possible.
------------------------------------------------------------
SqlAlchemy Database Datastore Backend
------------------------------------------------------------
This module allows one to use any database available through
the sqlalchemy package as a datastore for the modbus server.
This could be useful to have many servers who have data they
agree upon and is transactional.
------------------------------------------------------------
Redis Datastore Backend
------------------------------------------------------------
This module allows one to use redis as a modbus server
datastore backend. This achieves the same thing as the
sqlalchemy backend, however, it is much more lightweight and
easier to set up.
------------------------------------------------------------
Binary Coded Decimal Payload
------------------------------------------------------------
This module allows one to write binary coded decimal data to
the modbus server using the payload encoder/decoder
interfaces.
------------------------------------------------------------
Message Generator and Parser
------------------------------------------------------------
These are two utilities that can be used to create a number
of modbus messages for any of the available protocols as well
as to decode the messages and print descriptive text about
them.
Also included are a number of request and response messages
in tx-messages and rx-messages.
pymodbus-2.1.0/examples/contrib/asynchronous_asyncio_serial_client.py 0000775 0000000 0000000 00000012543 13355134677 0026407 0 ustar 00root root 0000000 0000000 from pymodbus.compat import IS_PYTHON3, PYTHON_VERSION
if IS_PYTHON3 and PYTHON_VERSION >= (3, 4):
import asyncio
from serial_asyncio import create_serial_connection
from pymodbus.client.async.asyncio import ModbusClientProtocol
from pymodbus.transaction import ModbusAsciiFramer, ModbusRtuFramer
from pymodbus.factory import ClientDecoder
else:
import sys
sys.stderr("This example needs to be run only on python 3.4 and above")
sys.exit(1)
# ----------------------------------------------------------------------- #
# configure the client logging
# ----------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
UNIT = 0x01
async def start_async_test(client):
# ----------------------------------------------------------------------- #
# specify slave to query
# ----------------------------------------------------------------------- #
# The slave to query is specified in an optional parameter for each
# individual request. This can be done by specifying the `unit` parameter
# which defaults to `0x00`
# ----------------------------------------------------------------------- #
log.debug("Reading Coils")
rr = client.read_coils(1, 1, unit=UNIT)
# ----------------------------------------------------------------------- #
# example requests
# ----------------------------------------------------------------------- #
# simply call the methods that you would like to use. An example session
# is displayed below along with some assert checks. Note that some modbus
# implementations differentiate holding/input discrete/coils and as such
# you will not be able to write to these, therefore the starting values
# are not known to these tests. Furthermore, some use the same memory
# blocks for the two sets, so a change to one is a change to the other.
# Keep both of these cases in mind when testing as the following will
# _only_ pass with the supplied async modbus server (script supplied).
# ----------------------------------------------------------------------- #
log.debug("Write to a Coil and read back")
rq = await client.write_coil(0, True, unit=UNIT)
rr = await client.read_coils(0, 1, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.bits[0] == True) # test the expected value
log.debug("Write to multiple coils and read back- test 1")
rq = await client.write_coils(1, [True]*8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
rr = await client.read_coils(1, 21, unit=UNIT)
assert(rr.function_code < 0x80) # test that we are not an error
resp = [True]*21
# If the returned output quantity is not a multiple of eight,
# the remaining bits in the final data byte will be padded with zeros
# (toward the high order end of the byte).
resp.extend([False]*3)
assert(rr.bits == resp) # test the expected value
log.debug("Write to multiple coils and read back - test 2")
rq = await client.write_coils(1, [False]*8, unit=UNIT)
rr = await client.read_coils(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.bits == [False]*8) # test the expected value
log.debug("Read discrete inputs")
rr = await client.read_discrete_inputs(0, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
log.debug("Write to a holding register and read back")
rq = await client.write_register(1, 10, unit=UNIT)
rr = await client.read_holding_registers(1, 1, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.registers[0] == 10) # test the expected value
log.debug("Write to multiple holding registers and read back")
rq = await client.write_registers(1, [10]*8, unit=UNIT)
rr = await client.read_holding_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rr.registers == [10]*8) # test the expected value
log.debug("Read input registers")
rr = await client.read_input_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
log.debug("Read write registeres simulataneously")
rq = await client.readwrite_registers(unit=UNIT, **arguments)
rr = await client.read_holding_registers(1, 8, unit=UNIT)
assert(rq.function_code < 0x80) # test that we are not an error
assert(rq.registers == [20]*8) # test the expected value
assert(rr.registers == [20]*8) # test the expected value
# create_serial_connection doesn't allow to pass arguments
# to protocol so that this is kind of workaround
def make_protocol():
return ModbusClientProtocol(framer=ModbusRtuFramer(ClientDecoder()))
if __name__ == '__main__':
loop = asyncio.get_event_loop()
coro = create_serial_connection(loop, make_protocol, '/dev/ptyp0',
baudrate=9600)
transport, protocol = loop.run_until_complete(asyncio.gather(coro))[0]
loop.run_until_complete(start_async_test(protocol))
loop.close()
pymodbus-2.1.0/examples/contrib/bcd_payload.py 0000664 0000000 0000000 00000015344 13355134677 0021472 0 ustar 00root root 0000000 0000000 """
Modbus BCD Payload Builder
-----------------------------------------------------------
This is an example of building a custom payload builder
that can be used in the pymodbus library. Below is a
simple binary coded decimal builder and decoder.
"""
from struct import pack, unpack
from pymodbus.constants import Endian
from pymodbus.interfaces import IPayloadBuilder
from pymodbus.utilities import pack_bitstring
from pymodbus.utilities import unpack_bitstring
from pymodbus.exceptions import ParameterException
from pymodbus.payload import BinaryPayloadDecoder
def convert_to_bcd(decimal):
""" Converts a decimal value to a bcd value
:param value: The decimal value to to pack into bcd
:returns: The number in bcd form
"""
place, bcd = 0, 0
while decimal > 0:
nibble = decimal % 10
bcd += nibble << place
decimal /= 10
place += 4
return bcd
def convert_from_bcd(bcd):
""" Converts a bcd value to a decimal value
:param value: The value to unpack from bcd
:returns: The number in decimal form
"""
place, decimal = 1, 0
while bcd > 0:
nibble = bcd & 0xf
decimal += nibble * place
bcd >>= 4
place *= 10
return decimal
def count_bcd_digits(bcd):
""" Count the number of digits in a bcd value
:param bcd: The bcd number to count the digits of
:returns: The number of digits in the bcd string
"""
count = 0
while bcd > 0:
count += 1
bcd >>= 4
return count
class BcdPayloadBuilder(IPayloadBuilder):
"""
A utility that helps build binary coded decimal payload
messages to be written with the various modbus messages.
example::
builder = BcdPayloadBuilder()
builder.add_number(1)
builder.add_number(int(2.234 * 1000))
payload = builder.build()
"""
def __init__(self, payload=None, endian=Endian.Little):
""" Initialize a new instance of the payload builder
:param payload: Raw payload data to initialize with
:param endian: The endianess of the payload
"""
self._payload = payload or []
self._endian = endian
def __str__(self):
""" Return the payload buffer as a string
:returns: The payload buffer as a string
"""
return ''.join(self._payload)
def reset(self):
""" Reset the payload buffer
"""
self._payload = []
def build(self):
""" Return the payload buffer as a list
This list is two bytes per element and can
thus be treated as a list of registers.
:returns: The payload buffer as a list
"""
string = str(self)
length = len(string)
string = string + ('\x00' * (length % 2))
return [string[i:i+2] for i in range(0, length, 2)]
def add_bits(self, values):
""" Adds a collection of bits to be encoded
If these are less than a multiple of eight,
they will be left padded with 0 bits to make
it so.
:param value: The value to add to the buffer
"""
value = pack_bitstring(values)
self._payload.append(value)
def add_number(self, value, size=None):
""" Adds any 8bit numeric type to the buffer
:param value: The value to add to the buffer
"""
encoded = []
value = convert_to_bcd(value)
size = size or count_bcd_digits(value)
while size > 0:
nibble = value & 0xf
encoded.append(pack('B', nibble))
value >>= 4
size -= 1
self._payload.extend(encoded)
def add_string(self, value):
""" Adds a string to the buffer
:param value: The value to add to the buffer
"""
self._payload.append(value)
class BcdPayloadDecoder(object):
"""
A utility that helps decode binary coded decimal payload
messages from a modbus reponse message. What follows is
a simple example::
decoder = BcdPayloadDecoder(payload)
first = decoder.decode_int(2)
second = decoder.decode_int(5) / 100
"""
def __init__(self, payload):
""" Initialize a new payload decoder
:param payload: The payload to decode with
"""
self._payload = payload
self._pointer = 0x00
@staticmethod
def fromRegisters(registers, endian=Endian.Little):
""" Initialize a payload decoder with the result of
reading a collection of registers from a modbus device.
The registers are treated as a list of 2 byte values.
We have to do this because of how the data has already
been decoded by the rest of the library.
:param registers: The register results to initialize with
:param endian: The endianess of the payload
:returns: An initialized PayloadDecoder
"""
if isinstance(registers, list): # repack into flat binary
payload = ''.join(pack('>H', x) for x in registers)
return BinaryPayloadDecoder(payload, endian)
raise ParameterException('Invalid collection of registers supplied')
@staticmethod
def fromCoils(coils, endian=Endian.Little):
""" Initialize a payload decoder with the result of
reading a collection of coils from a modbus device.
The coils are treated as a list of bit(boolean) values.
:param coils: The coil results to initialize with
:param endian: The endianess of the payload
:returns: An initialized PayloadDecoder
"""
if isinstance(coils, list):
payload = pack_bitstring(coils)
return BinaryPayloadDecoder(payload, endian)
raise ParameterException('Invalid collection of coils supplied')
def reset(self):
""" Reset the decoder pointer back to the start
"""
self._pointer = 0x00
def decode_int(self, size=1):
""" Decodes a int or long from the buffer
"""
self._pointer += size
handle = self._payload[self._pointer - size:self._pointer]
return convert_from_bcd(handle)
def decode_bits(self):
""" Decodes a byte worth of bits from the buffer
"""
self._pointer += 1
handle = self._payload[self._pointer - 1:self._pointer]
return unpack_bitstring(handle)
def decode_string(self, size=1):
""" Decodes a string from the buffer
:param size: The size of the string to decode
"""
self._pointer += size
return self._payload[self._pointer - size:self._pointer]
# --------------------------------------------------------------------------- #
# Exported Identifiers
# --------------------------------------------------------------------------- #
__all__ = ["BcdPayloadBuilder", "BcdPayloadDecoder"]
pymodbus-2.1.0/examples/contrib/concurrent_client.py 0000775 0000000 0000000 00000024025 13355134677 0022750 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Concurrent Modbus Client
---------------------------------------------------------------------------
This is an example of writing a high performance modbus client that allows
a high level of concurrency by using worker threads/processes to handle
writing/reading from one or more client handles at once.
"""
# -------------------------------------------------------------------------- #
# import system libraries
# -------------------------------------------------------------------------- #
import multiprocessing
import threading
import itertools
from collections import namedtuple
from pymodbus.compat import IS_PYTHON3
# we are using the future from the concurrent.futures released with
# python3. Alternatively we will try the backported library::
# pip install futures
try:
from concurrent.futures import Future
except ImportError:
from futures import Future
# -------------------------------------------------------------------------- #
# import neccessary modbus libraries
# -------------------------------------------------------------------------- #
from pymodbus.client.common import ModbusClientMixin
# -------------------------------------------------------------------------- #
# configure the client logging
# -------------------------------------------------------------------------- #
import logging
log = logging.getLogger("pymodbus")
log.setLevel(logging.DEBUG)
logging.basicConfig()
# -------------------------------------------------------------------------- #
# Initialize out concurrency primitives
# -------------------------------------------------------------------------- #
class _Primitives(object):
""" This is a helper class used to group the
threading primitives depending on the type of
worker situation we want to run (threads or processes).
"""
def __init__(self, **kwargs):
self.queue = kwargs.get('queue')
self.event = kwargs.get('event')
self.worker = kwargs.get('worker')
@classmethod
def create(cls, in_process=False):
""" Initialize a new instance of the concurrency
primitives.
:param in_process: True for threaded, False for processes
:returns: An initialized instance of concurrency primitives
"""
if in_process:
if IS_PYTHON3:
from queue import Queue
else:
from Queue import Queue
from threading import Thread
from threading import Event
return cls(queue=Queue, event=Event, worker=Thread)
else:
from multiprocessing import Queue
from multiprocessing import Event
from multiprocessing import Process
return cls(queue=Queue, event=Event, worker=Process)
# -------------------------------------------------------------------------- #
# Define our data transfer objects
# -------------------------------------------------------------------------- #
# These will be used to serialize state between the various workers.
# We use named tuples here as they are very lightweight while giving us
# all the benefits of classes.
# -------------------------------------------------------------------------- #
WorkRequest = namedtuple('WorkRequest', 'request, work_id')
WorkResponse = namedtuple('WorkResponse', 'is_exception, work_id, response')
# -------------------------------------------------------------------------- #
# Define our worker processes
# -------------------------------------------------------------------------- #
def _client_worker_process(factory, input_queue, output_queue, is_shutdown):
""" This worker process takes input requests, issues them on its
client handle, and then sends the client response (success or failure)
to the manager to deliver back to the application.
It should be noted that there are N of these workers and they can
be run in process or out of process as all the state serializes.
:param factory: A client factory used to create a new client
:param input_queue: The queue to pull new requests to issue
:param output_queue: The queue to place client responses
:param is_shutdown: Condition variable marking process shutdown
"""
log.info("starting up worker : %s", threading.current_thread())
client = factory()
while not is_shutdown.is_set():
try:
workitem = input_queue.get(timeout=1)
log.debug("dequeue worker request: %s", workitem)
if not workitem: continue
try:
log.debug("executing request on thread: %s", workitem)
result = client.execute(workitem.request)
output_queue.put(WorkResponse(False, workitem.work_id, result))
except Exception as exception:
log.exception("error in worker "
"thread: %s", threading.current_thread())
output_queue.put(WorkResponse(True,
workitem.work_id, exception))
except Exception as ex:
pass
log.info("request worker shutting down: %s", threading.current_thread())
def _manager_worker_process(output_queue, futures, is_shutdown):
""" This worker process manages taking output responses and
tying them back to the future keyed on the initial transaction id.
Basically this can be thought of as the delivery worker.
It should be noted that there are one of these threads and it must
be an in process thread as the futures will not serialize across
processes..
:param output_queue: The queue holding output results to return
:param futures: The mapping of tid -> future
:param is_shutdown: Condition variable marking process shutdown
"""
log.info("starting up manager worker: %s", threading.current_thread())
while not is_shutdown.is_set():
try:
workitem = output_queue.get()
future = futures.get(workitem.work_id, None)
log.debug("dequeue manager response: %s", workitem)
if not future: continue
if workitem.is_exception:
future.set_exception(workitem.response)
else: future.set_result(workitem.response)
log.debug("updated future result: %s", future)
del futures[workitem.work_id]
except Exception as ex:
log.exception("error in manager")
log.info("manager worker shutting down: %s", threading.current_thread())
# -------------------------------------------------------------------------- #
# Define our concurrent client
# -------------------------------------------------------------------------- #
class ConcurrentClient(ModbusClientMixin):
""" This is a high performance client that can be used
to read/write a large number of reqeusts at once asyncronously.
This operates with a backing worker pool of processes or threads
to achieve its performance.
"""
def __init__(self, **kwargs):
""" Initialize a new instance of the client
"""
worker_count = kwargs.get('count', multiprocessing.cpu_count())
self.factory = kwargs.get('factory')
primitives = _Primitives.create(kwargs.get('in_process', False))
self.is_shutdown = primitives.event() # process shutdown condition
self.input_queue = primitives.queue() # input requests to process
self.output_queue = primitives.queue() # output results to return
self.futures = {} # mapping of tid -> future
self.workers = [] # handle to our worker threads
self.counter = itertools.count()
# creating the response manager
self.manager = threading.Thread(
target=_manager_worker_process,
args=(self.output_queue, self.futures, self.is_shutdown)
)
self.manager.start()
self.workers.append(self.manager)
# creating the request workers
for i in range(worker_count):
worker = primitives.worker(
target=_client_worker_process,
args=(self.factory, self.input_queue, self.output_queue,
self.is_shutdown)
)
worker.start()
self.workers.append(worker)
def shutdown(self):
""" Shutdown all the workers being used to
concurrently process the requests.
"""
log.info("stating to shut down workers")
self.is_shutdown.set()
# to wake up the manager
self.output_queue.put(WorkResponse(None, None, None))
for worker in self.workers:
worker.join()
log.info("finished shutting down workers")
def execute(self, request):
""" Given a request, enqueue it to be processed
and then return a future linked to the response
of the call.
:param request: The request to execute
:returns: A future linked to the call's response
"""
if IS_PYTHON3:
fut, work_id = Future(), next(self.counter)
else:
fut, work_id = Future(), self.counter.next()
self.input_queue.put(WorkRequest(request, work_id))
self.futures[work_id] = fut
return fut
def execute_silently(self, request):
""" Given a write request, enqueue it to
be processed without worrying about calling the
application back (fire and forget)
:param request: The request to execute
"""
self.input_queue.put(WorkRequest(request, None))
if __name__ == "__main__":
from pymodbus.client.sync import ModbusTcpClient
def client_factory():
log.debug("creating client for: %s", threading.current_thread())
client = ModbusTcpClient('127.0.0.1', port=5020)
client.connect()
return client
client = ConcurrentClient(factory = client_factory)
try:
log.info("issuing concurrent requests")
futures = [client.read_coils(i * 8, 8) for i in range(10)]
log.info("waiting on futures to complete")
for future in futures:
log.info("future result: %s", future.result(timeout=1))
finally:
client.shutdown()
pymodbus-2.1.0/examples/contrib/deviceinfo_showcase_client.py 0000775 0000000 0000000 00000011764 13355134677 0024603 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Synchronous Client Example to showcase Device Information
--------------------------------------------------------------------------
This client demonstrates the use of Device Information to get information
about servers connected to the client. This is part of the MODBUS specification,
and uses the MEI 0x2B 0x0E request / response.
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
# from pymodbus.client.sync import ModbusUdpClient as ModbusClient
# from pymodbus.client.sync import ModbusSerialClient as ModbusClient
# --------------------------------------------------------------------------- #
# import the request
# --------------------------------------------------------------------------- #
from pymodbus.mei_message import ReadDeviceInformationRequest
from pymodbus.device import ModbusDeviceIdentification
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
UNIT = 0x1
def run_sync_client():
# ------------------------------------------------------------------------#
# choose the client you want
# ------------------------------------------------------------------------#
# make sure to start an implementation to hit against. For this
# you can use an existing device, the reference implementation in the tools
# directory, or start a pymodbus server.
#
# If you use the UDP or TCP clients, you can override the framer being used
# to use a custom implementation (say RTU over TCP). By default they use
# the socket framer::
#
# client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer)
#
# It should be noted that you can supply an ipv4 or an ipv6 host address
# for both the UDP and TCP clients.
#
# There are also other options that can be set on the client that controls
# how transactions are performed. The current ones are:
#
# * retries - Specify how many retries to allow per transaction (default=3)
# * retry_on_empty - Is an empty response a retry (default = False)
# * source_address - Specifies the TCP source address to bind to
#
# Here is an example of using these options::
#
# client = ModbusClient('localhost', retries=3, retry_on_empty=True)
# ------------------------------------------------------------------------#
client = ModbusClient('localhost', port=5020)
# from pymodbus.transaction import ModbusRtuFramer
# client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer)
# client = ModbusClient(method='binary', port='/dev/ptyp0', timeout=1)
# client = ModbusClient(method='ascii', port='/dev/ptyp0', timeout=1)
# client = ModbusClient(method='rtu', port='/dev/ptyp0', timeout=1,
# baudrate=9600)
client.connect()
# ------------------------------------------------------------------------#
# specify slave to query
# ------------------------------------------------------------------------#
# The slave to query is specified in an optional parameter for each
# individual request. This can be done by specifying the `unit` parameter
# which defaults to `0x00`
# ----------------------------------------------------------------------- #
log.debug("Reading Device Information")
information = {}
rr = None
while not rr or rr.more_follows:
next_object_id = rr.next_object_id if rr else 0
rq = ReadDeviceInformationRequest(read_code=0x03, unit=UNIT,
object_id=next_object_id)
rr = client.execute(rq)
information.update(rr.information)
log.debug(rr)
print("Device Information : ")
for key in information.keys():
print(key, information[key])
# ----------------------------------------------------------------------- #
# You can also have the information parsed through the
# ModbusDeviceIdentificiation class, which gets you a more usable way
# to access the Basic and Regular device information objects which are
# specifically listed in the Modbus specification
# ----------------------------------------------------------------------- #
di = ModbusDeviceIdentification(info=information)
print('Product Name : ', di.ProductName)
# ----------------------------------------------------------------------- #
# close the client
# ----------------------------------------------------------------------- #
client.close()
if __name__ == "__main__":
run_sync_client()
pymodbus-2.1.0/examples/contrib/deviceinfo_showcase_server.py 0000775 0000000 0000000 00000013015 13355134677 0024622 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Synchronous Server Example to showcase Device Information
--------------------------------------------------------------------------
This server demonstrates the use of Device Information to provide information
to clients about the device. This is part of the MODBUS specification, and
uses the MEI 0x2B 0x0E request / response. This example creates an otherwise
empty server.
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.server.sync import StartTcpServer
from pymodbus.server.sync import StartUdpServer
from pymodbus.server.sync import StartSerialServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusBinaryFramer
# --------------------------------------------------------------------------- #
# import versions of libraries which we will use later on for the example
# --------------------------------------------------------------------------- #
from pymodbus import __version__ as pymodbus_version
from serial import __version__ as pyserial_version
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
store = ModbusSlaveContext()
context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
# If you don't set this or any fields, they are defaulted to empty strings.
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '1.5'
# ----------------------------------------------------------------------- #
# Add an example which is long enough to force the ReadDeviceInformation
# request / response to require multiple responses to send back all of the
# information.
# ----------------------------------------------------------------------- #
identity[0x80] = "Lorem ipsum dolor sit amet, consectetur adipiscing " \
"elit. Vivamus rhoncus massa turpis, sit amet " \
"ultrices orci semper ut. Aliquam tristique sapien in " \
"lacus pharetra, in convallis nunc consectetur. Nunc " \
"velit elit, vehicula tempus tempus sed. "
# ----------------------------------------------------------------------- #
# Add an example with repeated object IDs. The MODBUS specification is
# entirely silent on whether or not this is allowed. In practice, this
# should be assumed to be contrary to the MODBUS specification and other
# clients (other than pymodbus) might behave differently when presented
# with an object ID occurring twice in the returned information.
#
# Use this at your discretion, and at the very least ensure that all
# objects which share a single object ID can fit together within a single
# ADU unit. In the case of Modbus RTU, this is about 240 bytes or so. In
# other words, when the spec says "An object is indivisible, therefore
# any object must have a size consistent with the size of transaction
# response", if you use repeated OIDs, apply that rule to the entire
# grouping of objects with the repeated OID.
# ----------------------------------------------------------------------- #
identity[0x81] = ['pymodbus {0}'.format(pymodbus_version),
'pyserial {0}'.format(pyserial_version)]
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
# Tcp:
StartTcpServer(context, identity=identity, address=("localhost", 5020))
# TCP with different framer
# StartTcpServer(context, identity=identity,
# framer=ModbusRtuFramer, address=("0.0.0.0", 5020))
# Udp:
# StartUdpServer(context, identity=identity, address=("0.0.0.0", 5020))
# Ascii:
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', timeout=1)
# RTU:
# StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
# port='/dev/ttyp0', timeout=.005, baudrate=9600)
# Binary
# StartSerialServer(context,
# identity=identity,
# framer=ModbusBinaryFramer,
# port='/dev/ttyp0',
# timeout=1)
if __name__ == "__main__":
run_server()
pymodbus-2.1.0/examples/contrib/libmodbus_client.py 0000775 0000000 0000000 00000043260 13355134677 0022550 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Libmodbus Protocol Wrapper
------------------------------------------------------------
What follows is an example wrapper of the libmodbus library
(http://libmodbus.org/documentation/) for use with pymodbus.
There are two utilities involved here:
* LibmodbusLevel1Client
This is simply a python wrapper around the c library. It is
mostly a clone of the pylibmodbus implementation, but I plan
on extending it to implement all the available protocol using
the raw execute methods.
* LibmodbusClient
This is just another modbus client that can be used just like
any other client in pymodbus.
For these to work, you must have `cffi` and `libmodbus-dev` installed:
sudo apt-get install libmodbus-dev
pip install cffi
"""
# -------------------------------------------------------------------------- #
# import system libraries
# -------------------------------------------------------------------------- #
from cffi import FFI
# -------------------------------------------------------------------------- #
# import pymodbus libraries
# -------------------------------------------------------------------------- #
from pymodbus.constants import Defaults
from pymodbus.exceptions import ModbusException
from pymodbus.client.common import ModbusClientMixin
from pymodbus.bit_read_message import ReadCoilsResponse, ReadDiscreteInputsResponse
from pymodbus.register_read_message import ReadHoldingRegistersResponse, ReadInputRegistersResponse
from pymodbus.register_read_message import ReadWriteMultipleRegistersResponse
from pymodbus.bit_write_message import WriteSingleCoilResponse, WriteMultipleCoilsResponse
from pymodbus.register_write_message import WriteSingleRegisterResponse, WriteMultipleRegistersResponse
# --------------------------------------------------------------------------- #
# create the C interface
# --------------------------------------------------------------------------- #
# * TODO add the protocol needed for the servers
# --------------------------------------------------------------------------- #
compiler = FFI()
compiler.cdef("""
typedef struct _modbus modbus_t;
int modbus_connect(modbus_t *ctx);
int modbus_flush(modbus_t *ctx);
void modbus_close(modbus_t *ctx);
const char *modbus_strerror(int errnum);
int modbus_set_slave(modbus_t *ctx, int slave);
void modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
void modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
int modbus_write_register(modbus_t *ctx, int reg_addr, int value);
int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb, const uint16_t *src, int read_addr, int read_nb, uint16_t *dest);
int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length);
float modbus_get_float(const uint16_t *src);
void modbus_set_float(float f, uint16_t *dest);
modbus_t* modbus_new_tcp(const char *ip_address, int port);
modbus_t* modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);
void modbus_free(modbus_t *ctx);
int modbus_receive(modbus_t *ctx, uint8_t *req);
int modbus_receive_from(modbus_t *ctx, int sockfd, uint8_t *req);
int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
""")
LIB = compiler.dlopen('modbus') # create our bindings
# -------------------------------------------------------------------------- #
# helper utilites
# -------------------------------------------------------------------------- #
def get_float(data):
return LIB.modbus_get_float(data)
def set_float(value, data):
LIB.modbus_set_float(value, data)
def cast_to_int16(data):
return int(compiler.cast('int16_t', data))
def cast_to_int32(data):
return int(compiler.cast('int32_t', data))
class NotImplementedException(Exception):
pass
# -------------------------------------------------------------------------- #
# level1 client
# -------------------------------------------------------------------------- #
class LibmodbusLevel1Client(object):
""" A raw wrapper around the libmodbus c library. Feel free
to use it if you want increased performance and don't mind the
entire protocol not being implemented.
"""
@classmethod
def create_tcp_client(klass, host='127.0.0.1', port=Defaults.Port):
""" Create a TCP modbus client for the supplied parameters.
:param host: The host to connect to
:param port: The port to connect to on that host
:returns: A new level1 client
"""
client = LIB.modbus_new_tcp(host.encode(), port)
return klass(client)
@classmethod
def create_rtu_client(klass, **kwargs):
""" Create a TCP modbus client for the supplied parameters.
:param port: The serial port to attach to
:param stopbits: The number of stop bits to use
:param bytesize: The bytesize of the serial messages
:param parity: Which kind of parity to use
:param baudrate: The baud rate to use for the serial device
:returns: A new level1 client
"""
port = kwargs.get('port', '/dev/ttyS0')
baudrate = kwargs.get('baud', Defaults.Baudrate)
parity = kwargs.get('parity', Defaults.Parity)
bytesize = kwargs.get('bytesize', Defaults.Bytesize)
stopbits = kwargs.get('stopbits', Defaults.Stopbits)
client = LIB.modbus_new_rtu(port, baudrate, parity, bytesize, stopbits)
return klass(client)
def __init__(self, client):
""" Initalize a new instance of the LibmodbusLevel1Client. This
method should not be used, instead new instances should be created
using the two supplied factory methods:
* LibmodbusLevel1Client.create_rtu_client(...)
* LibmodbusLevel1Client.create_tcp_client(...)
:param client: The underlying client instance to operate with.
"""
self.client = client
self.slave = Defaults.UnitId
def set_slave(self, slave):
""" Set the current slave to operate against.
:param slave: The new slave to operate against
:returns: The resulting slave to operate against
"""
self.slave = self._execute(LIB.modbus_set_slave, slave)
return self.slave
def connect(self):
""" Attempt to connect to the client target.
:returns: True if successful, throws otherwise
"""
return (self.__execute(LIB.modbus_connect) == 0)
def flush(self):
""" Discards the existing bytes on the wire.
:returns: The number of flushed bytes, or throws
"""
return self.__execute(LIB.modbus_flush)
def close(self):
""" Closes and frees the underlying connection
and context structure.
:returns: Always True
"""
LIB.modbus_close(self.client)
LIB.modbus_free(self.client)
return True
def __execute(self, command, *args):
""" Run the supplied command against the currently
instantiated client with the supplied arguments. This
will make sure to correctly handle resulting errors.
:param command: The command to execute against the context
:param *args: The arguments for the given command
:returns: The result of the operation unless -1 which throws
"""
result = command(self.client, *args)
if result == -1:
message = LIB.modbus_strerror(compiler.errno)
raise ModbusException(compiler.string(message))
return result
def read_bits(self, address, count=1):
"""
:param address: The starting address to read from
:param count: The number of coils to read
:returns: The resulting bits
"""
result = compiler.new("uint8_t[]", count)
self.__execute(LIB.modbus_read_bits, address, count, result)
return result
def read_input_bits(self, address, count=1):
"""
:param address: The starting address to read from
:param count: The number of discretes to read
:returns: The resulting bits
"""
result = compiler.new("uint8_t[]", count)
self.__execute(LIB.modbus_read_input_bits, address, count, result)
return result
def write_bit(self, address, value):
"""
:param address: The starting address to write to
:param value: The value to write to the specified address
:returns: The number of written bits
"""
return self.__execute(LIB.modbus_write_bit, address, value)
def write_bits(self, address, values):
"""
:param address: The starting address to write to
:param values: The values to write to the specified address
:returns: The number of written bits
"""
count = len(values)
return self.__execute(LIB.modbus_write_bits, address, count, values)
def write_register(self, address, value):
"""
:param address: The starting address to write to
:param value: The value to write to the specified address
:returns: The number of written registers
"""
return self.__execute(LIB.modbus_write_register, address, value)
def write_registers(self, address, values):
"""
:param address: The starting address to write to
:param values: The values to write to the specified address
:returns: The number of written registers
"""
count = len(values)
return self.__execute(LIB.modbus_write_registers, address, count, values)
def read_registers(self, address, count=1):
"""
:param address: The starting address to read from
:param count: The number of registers to read
:returns: The resulting read registers
"""
result = compiler.new("uint16_t[]", count)
self.__execute(LIB.modbus_read_registers, address, count, result)
return result
def read_input_registers(self, address, count=1):
"""
:param address: The starting address to read from
:param count: The number of registers to read
:returns: The resulting read registers
"""
result = compiler.new("uint16_t[]", count)
self.__execute(LIB.modbus_read_input_registers, address, count, result)
return result
def read_and_write_registers(self, read_address, read_count, write_address, write_registers):
"""
:param read_address: The address to start reading from
:param read_count: The number of registers to read from address
:param write_address: The address to start writing to
:param write_registers: The registers to write to the specified address
:returns: The resulting read registers
"""
write_count = len(write_registers)
read_result = compiler.new("uint16_t[]", read_count)
self.__execute(LIB.modbus_write_and_read_registers,
write_address, write_count, write_registers,
read_address, read_count, read_result)
return read_result
# -------------------------------------------------------------------------- #
# level2 client
# -------------------------------------------------------------------------- #
class LibmodbusClient(ModbusClientMixin):
""" A facade around the raw level 1 libmodbus client
that implements the pymodbus protocol on top of the lower level
client.
"""
# ----------------------------------------------------------------------- #
# these are used to convert from the pymodbus request types to the
# libmodbus operations (overloaded operator).
# ----------------------------------------------------------------------- #
__methods = {
'ReadCoilsRequest': lambda c, r: c.read_bits(r.address, r.count),
'ReadDiscreteInputsRequest': lambda c, r: c.read_input_bits(r.address,
r.count),
'WriteSingleCoilRequest': lambda c, r: c.write_bit(r.address,
r.value),
'WriteMultipleCoilsRequest': lambda c, r: c.write_bits(r.address,
r.values),
'WriteSingleRegisterRequest': lambda c, r: c.write_register(r.address,
r.value),
'WriteMultipleRegistersRequest':
lambda c, r: c.write_registers(r.address, r.values),
'ReadHoldingRegistersRequest':
lambda c, r: c.read_registers(r.address, r.count),
'ReadInputRegistersRequest':
lambda c, r: c.read_input_registers(r.address, r.count),
'ReadWriteMultipleRegistersRequest':
lambda c, r: c.read_and_write_registers(r.read_address,
r.read_count,
r.write_address,
r.write_registers),
}
# ----------------------------------------------------------------------- #
# these are used to convert from the libmodbus result to the
# pymodbus response type
# ----------------------------------------------------------------------- #
__adapters = {
'ReadCoilsRequest':
lambda tx, rx: ReadCoilsResponse(list(rx)),
'ReadDiscreteInputsRequest':
lambda tx, rx: ReadDiscreteInputsResponse(list(rx)),
'WriteSingleCoilRequest':
lambda tx, rx: WriteSingleCoilResponse(tx.address, rx),
'WriteMultipleCoilsRequest':
lambda tx, rx: WriteMultipleCoilsResponse(tx.address, rx),
'WriteSingleRegisterRequest':
lambda tx, rx: WriteSingleRegisterResponse(tx.address, rx),
'WriteMultipleRegistersRequest':
lambda tx, rx: WriteMultipleRegistersResponse(tx.address, rx),
'ReadHoldingRegistersRequest':
lambda tx, rx: ReadHoldingRegistersResponse(list(rx)),
'ReadInputRegistersRequest':
lambda tx, rx: ReadInputRegistersResponse(list(rx)),
'ReadWriteMultipleRegistersRequest':
lambda tx, rx: ReadWriteMultipleRegistersResponse(list(rx)),
}
def __init__(self, client):
""" Initalize a new instance of the LibmodbusClient. This should
be initialized with one of the LibmodbusLevel1Client instances:
* LibmodbusLevel1Client.create_rtu_client(...)
* LibmodbusLevel1Client.create_tcp_client(...)
:param client: The underlying client instance to operate with.
"""
self.client = client
# ----------------------------------------------------------------------- #
# We use the client mixin to implement the api methods which are all
# forwarded to this method. It is implemented using the previously
# defined lookup tables. Any method not defined simply throws.
# ----------------------------------------------------------------------- #
def execute(self, request):
""" Execute the supplied request against the server.
:param request: The request to process
:returns: The result of the request execution
"""
if self.client.slave != request.unit_id:
self.client.set_slave(request.unit_id)
method = request.__class__.__name__
operation = self.__methods.get(method, None)
adapter = self.__adapters.get(method, None)
if not operation or not adapter:
raise NotImplementedException("Method not "
"implemented: " + operation)
response = operation(self.client, request)
return adapter(request, response)
# ----------------------------------------------------------------------- #
# Other methods can simply be forwarded using the decorator pattern
# ----------------------------------------------------------------------- #
def connect(self):
return self.client.connect()
def close(self):
return self.client.close()
# ----------------------------------------------------------------------- #
# magic methods
# ----------------------------------------------------------------------- #
def __enter__(self):
""" Implement the client with enter block
:returns: The current instance of the client
"""
self.client.connect()
return self
def __exit__(self, klass, value, traceback):
""" Implement the client with exit block """
self.client.close()
# -------------------------------------------------------------------------- #
# main example runner
# -------------------------------------------------------------------------- #
if __name__ == '__main__':
# create our low level client
host = '127.0.0.1'
port = 502
protocol = LibmodbusLevel1Client.create_tcp_client(host, port)
# operate with our high level client
with LibmodbusClient(protocol) as client:
registers = client.write_registers(0, [13, 12, 11])
print(registers)
registers = client.read_holding_registers(0, 10)
print(registers.registers)
pymodbus-2.1.0/examples/contrib/message_generator.py 0000775 0000000 0000000 00000017611 13355134677 0022725 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Modbus Message Generator
--------------------------------------------------------------------------
The following is an example of how to generate example encoded messages
for the supplied modbus format:
* tcp - `./generate-messages.py -f tcp -m rx -b`
* ascii - `./generate-messages.py -f ascii -m tx -a`
* rtu - `./generate-messages.py -f rtu -m rx -b`
* binary - `./generate-messages.py -f binary -m tx -b`
"""
from optparse import OptionParser
import codecs as c
# -------------------------------------------------------------------------- #
# import all the available framers
# -------------------------------------------------------------------------- #
from pymodbus.transaction import ModbusSocketFramer
from pymodbus.transaction import ModbusBinaryFramer
from pymodbus.transaction import ModbusAsciiFramer
from pymodbus.transaction import ModbusRtuFramer
# -------------------------------------------------------------------------- #
# import all available messages
# -------------------------------------------------------------------------- #
from pymodbus.bit_read_message import *
from pymodbus.bit_write_message import *
from pymodbus.diag_message import *
from pymodbus.file_message import *
from pymodbus.other_message import *
from pymodbus.mei_message import *
from pymodbus.register_read_message import *
from pymodbus.register_write_message import *
from pymodbus.compat import IS_PYTHON3
# -------------------------------------------------------------------------- #
# initialize logging
# -------------------------------------------------------------------------- #
import logging
modbus_log = logging.getLogger("pymodbus")
# -------------------------------------------------------------------------- #
# enumerate all request messages
# -------------------------------------------------------------------------- #
_request_messages = [
ReadHoldingRegistersRequest,
ReadDiscreteInputsRequest,
ReadInputRegistersRequest,
ReadCoilsRequest,
WriteMultipleCoilsRequest,
WriteMultipleRegistersRequest,
WriteSingleRegisterRequest,
WriteSingleCoilRequest,
ReadWriteMultipleRegistersRequest,
ReadExceptionStatusRequest,
GetCommEventCounterRequest,
GetCommEventLogRequest,
ReportSlaveIdRequest,
ReadFileRecordRequest,
WriteFileRecordRequest,
MaskWriteRegisterRequest,
ReadFifoQueueRequest,
ReadDeviceInformationRequest,
ReturnQueryDataRequest,
RestartCommunicationsOptionRequest,
ReturnDiagnosticRegisterRequest,
ChangeAsciiInputDelimiterRequest,
ForceListenOnlyModeRequest,
ClearCountersRequest,
ReturnBusMessageCountRequest,
ReturnBusCommunicationErrorCountRequest,
ReturnBusExceptionErrorCountRequest,
ReturnSlaveMessageCountRequest,
ReturnSlaveNoResponseCountRequest,
ReturnSlaveNAKCountRequest,
ReturnSlaveBusyCountRequest,
ReturnSlaveBusCharacterOverrunCountRequest,
ReturnIopOverrunCountRequest,
ClearOverrunCountRequest,
GetClearModbusPlusRequest
]
# -------------------------------------------------------------------------- #
# enumerate all response messages
# -------------------------------------------------------------------------- #
_response_messages = [
ReadHoldingRegistersResponse,
ReadDiscreteInputsResponse,
ReadInputRegistersResponse,
ReadCoilsResponse,
WriteMultipleCoilsResponse,
WriteMultipleRegistersResponse,
WriteSingleRegisterResponse,
WriteSingleCoilResponse,
ReadWriteMultipleRegistersResponse,
ReadExceptionStatusResponse,
GetCommEventCounterResponse,
GetCommEventLogResponse,
ReportSlaveIdResponse,
ReadFileRecordResponse,
WriteFileRecordResponse,
MaskWriteRegisterResponse,
ReadFifoQueueResponse,
ReadDeviceInformationResponse,
ReturnQueryDataResponse,
RestartCommunicationsOptionResponse,
ReturnDiagnosticRegisterResponse,
ChangeAsciiInputDelimiterResponse,
ForceListenOnlyModeResponse,
ClearCountersResponse,
ReturnBusMessageCountResponse,
ReturnBusCommunicationErrorCountResponse,
ReturnBusExceptionErrorCountResponse,
ReturnSlaveMessageCountResponse,
ReturnSlaveNoReponseCountResponse,
ReturnSlaveNAKCountResponse,
ReturnSlaveBusyCountResponse,
ReturnSlaveBusCharacterOverrunCountResponse,
ReturnIopOverrunCountResponse,
ClearOverrunCountResponse,
GetClearModbusPlusResponse
]
# -------------------------------------------------------------------------- #
# build an arguments singleton
# -------------------------------------------------------------------------- #
# Feel free to override any values here to generate a specific message
# in question. It should be noted that many argument names are reused
# between different messages, and a number of messages are simply using
# their default values.
# -------------------------------------------------------------------------- #
_arguments = {
'address': 0x12,
'count': 0x08,
'value': 0x01,
'values': [0x01] * 8,
'read_address': 0x12,
'read_count': 0x08,
'write_address': 0x12,
'write_registers': [0x01] * 8,
'transaction': 0x01,
'protocol': 0x00,
'unit': 0xff,
}
# -------------------------------------------------------------------------- #
# generate all the requested messages
# -------------------------------------------------------------------------- #
def generate_messages(framer, options):
""" A helper method to parse the command line options
:param framer: The framer to encode the messages with
:param options: The message options to use
"""
if options.messages == "tx":
messages = _request_messages
else:
messages = _response_messages
for message in messages:
message = message(**_arguments)
print("%-44s = " % message.__class__.__name__)
packet = framer.buildPacket(message)
if not options.ascii:
if not IS_PYTHON3:
packet = packet.encode('hex')
else:
packet = c.encode(packet, 'hex_codec').decode('utf-8')
print ("{}\n".format(packet)) # because ascii ends with a \r\n
# -------------------------------------------------------------------------- #
# initialize our program settings
# -------------------------------------------------------------------------- #
def get_options():
""" A helper method to parse the command line options
:returns: The options manager
"""
parser = OptionParser()
parser.add_option("-f", "--framer",
help="The type of framer to use "
"(tcp, rtu, binary, ascii)",
dest="framer", default="tcp")
parser.add_option("-D", "--debug",
help="Enable debug tracing",
action="store_true", dest="debug", default=False)
parser.add_option("-a", "--ascii",
help="The indicates that the message is ascii",
action="store_true", dest="ascii", default=True)
parser.add_option("-b", "--binary",
help="The indicates that the message is binary",
action="store_false", dest="ascii")
parser.add_option("-m", "--messages",
help="The messages to encode (rx, tx)",
dest="messages", default='rx')
(opt, arg) = parser.parse_args()
return opt
def main():
""" The main runner function
"""
option = get_options()
if option.debug:
try:
modbus_log.setLevel(logging.DEBUG)
logging.basicConfig()
except Exception as e:
print("Logging is not supported on this system")
framer = lookup = {
'tcp': ModbusSocketFramer,
'rtu': ModbusRtuFramer,
'binary': ModbusBinaryFramer,
'ascii': ModbusAsciiFramer,
}.get(option.framer, ModbusSocketFramer)(None)
generate_messages(framer, option)
if __name__ == "__main__":
main()
pymodbus-2.1.0/examples/contrib/message_parser.py 0000775 0000000 0000000 00000016326 13355134677 0022235 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Modbus Message Parser
--------------------------------------------------------------------------
The following is an example of how to parse modbus messages
using the supplied framers for a number of protocols:
* tcp
* ascii
* rtu
* binary
"""
# -------------------------------------------------------------------------- #
# import needed libraries
# -------------------------------------------------------------------------- #
from __future__ import print_function
import collections
import textwrap
from optparse import OptionParser
import codecs as c
from pymodbus.factory import ClientDecoder, ServerDecoder
from pymodbus.transaction import ModbusSocketFramer
from pymodbus.transaction import ModbusBinaryFramer
from pymodbus.transaction import ModbusAsciiFramer
from pymodbus.transaction import ModbusRtuFramer
from pymodbus.compat import IS_PYTHON3
# -------------------------------------------------------------------------- #
# -------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
# -------------------------------------------------------------------------- #
# build a quick wrapper around the framers
# -------------------------------------------------------------------------- #
class Decoder(object):
def __init__(self, framer, encode=False):
""" Initialize a new instance of the decoder
:param framer: The framer to use
:param encode: If the message needs to be encoded
"""
self.framer = framer
self.encode = encode
def decode(self, message):
""" Attempt to decode the supplied message
:param message: The messge to decode
"""
if IS_PYTHON3:
value = message if self.encode else c.encode(message, 'hex_codec')
else:
value = message if self.encode else message.encode('hex')
print("="*80)
print("Decoding Message %s" % value)
print("="*80)
decoders = [
self.framer(ServerDecoder(), client=None),
self.framer(ClientDecoder(), client=None)
]
for decoder in decoders:
print("%s" % decoder.decoder.__class__.__name__)
print("-"*80)
try:
decoder.addToFrame(message)
if decoder.checkFrame():
unit = decoder._header.get("uid", 0x00)
decoder.advanceFrame()
decoder.processIncomingPacket(message, self.report, unit)
else:
self.check_errors(decoder, message)
except Exception as ex:
self.check_errors(decoder, message)
def check_errors(self, decoder, message):
""" Attempt to find message errors
:param message: The message to find errors in
"""
log.error("Unable to parse message - {} with {}".format(message,
decoder))
def report(self, message):
""" The callback to print the message information
:param message: The message to print
"""
print("%-15s = %s" % ('name', message.__class__.__name__))
for (k, v) in message.__dict__.items():
if isinstance(v, dict):
print("%-15s =" % k)
for kk,vv in v.items():
print(" %-12s => %s" % (kk, vv))
elif isinstance(v, collections.Iterable):
print("%-15s =" % k)
value = str([int(x) for x in v])
for line in textwrap.wrap(value, 60):
print("%-15s . %s" % ("", line))
else:
print("%-15s = %s" % (k, hex(v)))
print("%-15s = %s" % ('documentation', message.__doc__))
# -------------------------------------------------------------------------- #
# and decode our message
# -------------------------------------------------------------------------- #
def get_options():
""" A helper method to parse the command line options
:returns: The options manager
"""
parser = OptionParser()
parser.add_option("-p", "--parser",
help="The type of parser to use "
"(tcp, rtu, binary, ascii)",
dest="parser", default="tcp")
parser.add_option("-D", "--debug",
help="Enable debug tracing",
action="store_true", dest="debug", default=False)
parser.add_option("-m", "--message",
help="The message to parse",
dest="message", default=None)
parser.add_option("-a", "--ascii",
help="The indicates that the message is ascii",
action="store_true", dest="ascii", default=True)
parser.add_option("-b", "--binary",
help="The indicates that the message is binary",
action="store_false", dest="ascii")
parser.add_option("-f", "--file",
help="The file containing messages to parse",
dest="file", default=None)
parser.add_option("-t", "--transaction",
help="If the incoming message is in hexadecimal format",
action="store_true", dest="transaction", default=False)
(opt, arg) = parser.parse_args()
if not opt.message and len(arg) > 0:
opt.message = arg[0]
return opt
def get_messages(option):
""" A helper method to generate the messages to parse
:param options: The option manager
:returns: The message iterator to parse
"""
if option.message:
if option.transaction:
msg = ""
for segment in option.message.split():
segment = segment.replace("0x", "")
segment = "0" + segment if len(segment) == 1 else segment
msg = msg + segment
option.message = msg
if not option.ascii:
if not IS_PYTHON3:
option.message = option.message.decode('hex')
else:
option.message = c.decode(option.message.encode(), 'hex_codec')
yield option.message
elif option.file:
with open(option.file, "r") as handle:
for line in handle:
if line.startswith('#'): continue
if not option.ascii:
line = line.strip()
line = line.decode('hex')
yield line
def main():
""" The main runner function
"""
option = get_options()
if option.debug:
try:
modbus_log.setLevel(logging.DEBUG)
logging.basicConfig()
except Exception as e:
print("Logging is not supported on this system- {}".format(e))
framer = lookup = {
'tcp': ModbusSocketFramer,
'rtu': ModbusRtuFramer,
'binary': ModbusBinaryFramer,
'ascii': ModbusAsciiFramer,
}.get(option.parser, ModbusSocketFramer)
decoder = Decoder(framer, option.ascii)
for message in get_messages(option):
decoder.decode(message)
if __name__ == "__main__":
main()
pymodbus-2.1.0/examples/contrib/modbus_mapper.py 0000664 0000000 0000000 00000025560 13355134677 0022067 0 ustar 00root root 0000000 0000000 """
Given a modbus mapping file, this is used to generate
decoder blocks so that non-programmers can define the
register values and then decode a modbus device all
without having to write a line of code for decoding.
Currently supported formats are:
* csv
* json
* xml
Here is an example of generating and using a mapping decoder
(note that this is still in the works and will be greatly
simplified in the final api; it is just an example of the
requested functionality)::
from modbus_mapper import csv_mapping_parser
from modbus_mapper import mapping_decoder
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.payload import BinaryModbusDecoder
template = ['address', 'size', 'function', 'name', 'description']
raw_mapping = csv_mapping_parser('input.csv', template)
mapping = mapping_decoder(raw_mapping)
index, size = 1, 100
client = ModbusTcpClient('localhost')
response = client.read_holding_registers(index, size)
decoder = BinaryModbusDecoder.fromRegisters(response.registers)
while index < size:
print "[{}]\t{}".format(i, mapping[i]['type'](decoder))
index += mapping[i]['size']
Also, using the same input mapping parsers, we can generate
populated slave contexts that can be run behing a modbus server::
from modbus_mapper import csv_mapping_parser
from modbus_mapper import modbus_context_decoder
from pymodbus.client.ssync import StartTcpServer
from pymodbus.datastore.context import ModbusServerContext
template = ['address', 'value', 'function', 'name', 'description']
raw_mapping = csv_mapping_parser('input.csv', template)
slave_context = modbus_context_decoder(raw_mapping)
context = ModbusServerContext(slaves=slave_context, single=True)
StartTcpServer(context)
"""
import csv
import json
from collections import defaultdict
from tokenize import generate_tokens
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.datastore.store import ModbusSparseDataBlock
from pymodbus.compat import IS_PYTHON3
from pymodbus.datastore.context import ModbusSlaveContext
if IS_PYTHON3:
from io import StringIO
else:
from StringIO import StringIO
# --------------------------------------------------------------------------- #
# raw mapping input parsers
# --------------------------------------------------------------------------- #
# These generate the raw mapping_blocks from some form of input
# which can then be passed to the decoder in question to supply
# the requested output result.
# --------------------------------------------------------------------------- #
def csv_mapping_parser(path, template):
""" Given a csv file of the the mapping data for
a modbus device, return a mapping layout that can
be used to decode an new block.
.. note:: For the template, a few values are required
to be defined: address, size, function, and type. All the remaining
values will be stored, but not formatted by the application.
So for example::
template = ['address', 'type', 'size', 'name', 'function']
mappings = json_mapping_parser('mapping.json', template)
:param path: The path to the csv input file
:param template: The row value template
:returns: The decoded csv dictionary
"""
mapping_blocks = defaultdict(dict)
with open(path, 'r') as handle:
reader = csv.reader(handle)
reader.next() # skip the csv header
for row in reader:
mapping = dict(zip(template, row))
fid = mapping.pop('function')
aid = int(mapping['address'])
mapping_blocks[aid] = mapping
return mapping_blocks
def json_mapping_parser(path, template):
""" Given a json file of the the mapping data for
a modbus device, return a mapping layout that can
be used to decode an new block.
.. note:: For the template, a few values are required
to be mapped: address, size, and type. All the remaining
values will be stored, but not formatted by the application.
So for example::
template = {
'Start': 'address',
'DataType': 'type',
'Length': 'size'
# the remaining keys will just pass through
}
mappings = json_mapping_parser('mapping.json', template)
:param path: The path to the csv input file
:param template: The row value template
:returns: The decoded csv dictionary
"""
mapping_blocks = {}
with open(path, 'r') as handle:
for tid, rows in json.load(handle).iteritems():
mappings = {}
for key, values in rows.iteritems():
mapping = {template.get(k, k) : v for k, v in values.iteritems()}
mappings[int(key)] = mapping
mapping_blocks[tid] = mappings
return mapping_blocks
def xml_mapping_parser(path):
""" Given an xml file of the the mapping data for
a modbus device, return a mapping layout that can
be used to decode an new block.
.. note:: The input of the xml file is defined as
follows::
:param path: The path to the xml input file
:returns: The decoded csv dictionary
"""
pass
# --------------------------------------------------------------------------- #
# modbus context decoders
# --------------------------------------------------------------------------- #
# These are used to decode a raw mapping_block into a slave context with
# populated function data blocks.
# --------------------------------------------------------------------------- #
def modbus_context_decoder(mapping_blocks):
""" Given a mapping block input, generate a backing
slave context with initialized data blocks.
.. note:: This expects the following for each block:
address, value, and function where function is one of
di (discretes), co (coils), hr (holding registers), or
ir (input registers).
:param mapping_blocks: The mapping blocks
:returns: The initialized modbus slave context
"""
blocks = defaultdict(dict)
for block in mapping_blocks.itervalues():
for mapping in block.itervalues():
value = int(mapping['value'])
address = int(mapping['address'])
function = mapping['function']
blocks[function][address] = value
return ModbusSlaveContext(**blocks)
# --------------------------------------------------------------------------- #
# modbus mapping decoder
# --------------------------------------------------------------------------- #
# These are used to decode a raw mapping_block into a request decoder.
# So this allows one to simply grab a number of registers, and then
# pass them to this decoder which will do the rest.
# --------------------------------------------------------------------------- #
class ModbusTypeDecoder(object):
""" This is a utility to determine the correct
decoder to use given a type name. By default this
supports all the types available in the default modbus
decoder, however this can easily be extended this class
and adding new types to the mapper::
class CustomTypeDecoder(ModbusTypeDecoder):
def __init__(self):
ModbusTypeDecode.__init__(self)
self.mapper['type-token'] = self.callback
def parse_my_bitfield(self, tokens):
return lambda d: d.decode_my_type()
"""
def __init__(self):
""" Initializes a new instance of the decoder
"""
self.default = lambda m: self.parse_16bit_uint
self.parsers = {
'uint': self.parse_16bit_uint,
'uint8': self.parse_8bit_uint,
'uint16': self.parse_16bit_uint,
'uint32': self.parse_32bit_uint,
'uint64': self.parse_64bit_uint,
'int': self.parse_16bit_int,
'int8': self.parse_8bit_int,
'int16': self.parse_16bit_int,
'int32': self.parse_32bit_int,
'int64': self.parse_64bit_int,
'float': self.parse_32bit_float,
'float32': self.parse_32bit_float,
'float64': self.parse_64bit_float,
'string': self.parse_32bit_int,
'bits': self.parse_bits,
}
# ------------------------------------------------------------ #
# Type parsers
# ------------------------------------------------------------ #
@staticmethod
def parse_string(tokens):
_ = tokens.next()
size = int(tokens.next())
return lambda d: d.decode_string(size=size)
@staticmethod
def parse_bits(tokens):
return lambda d: d.decode_bits()
@staticmethod
def parse_8bit_uint(tokens):
return lambda d: d.decode_8bit_uint()
@staticmethod
def parse_16bit_uint(tokens):
return lambda d: d.decode_16bit_uint()
@staticmethod
def parse_32bit_uint(tokens):
return lambda d: d.decode_32bit_uint()
@staticmethod
def parse_64bit_uint(tokens):
return lambda d: d.decode_64bit_uint()
@staticmethod
def parse_8bit_int(tokens):
return lambda d: d.decode_8bit_int()
@staticmethod
def parse_16bit_int(tokens):
return lambda d: d.decode_16bit_int()
@staticmethod
def parse_32bit_int(tokens):
return lambda d: d.decode_32bit_int()
@staticmethod
def parse_64bit_int(tokens):
return lambda d: d.decode_64bit_int()
@staticmethod
def parse_32bit_float(tokens):
return lambda d: d.decode_32bit_float()
@staticmethod
def parse_64bit_float(tokens):
return lambda d: d.decode_64bit_float()
#------------------------------------------------------------
# Public Interface
#------------------------------------------------------------
def tokenize(self, value):
""" Given a value, return the tokens
:param value: The value to tokenize
:returns: A token generator
"""
tokens = generate_tokens(StringIO(value).readline)
for toknum, tokval, _, _, _ in tokens:
yield tokval
def parse(self, value):
""" Given a type value, return a function
that supplied with a decoder, will decode
the correct value.
:param value: The type of value to parse
:returns: The decoder method to use
"""
tokens = self.tokenize(value)
token = tokens.next().lower()
parser = self.parsers.get(token, self.default)
return parser(tokens)
def mapping_decoder(mapping_blocks, decoder=None):
""" Given the raw mapping blocks, convert
them into modbus value decoder map.
:param mapping_blocks: The mapping blocks
:param decoder: The type decoder to use
"""
decoder = decoder or ModbusTypeDecoder()
for block in mapping_blocks.itervalues():
for mapping in block.itervalues():
mapping['address'] = int(mapping['address'])
mapping['size'] = int(mapping['size'])
mapping['type'] = decoder.parse(mapping['type'])
pymodbus-2.1.0/examples/contrib/modbus_saver.py 0000664 0000000 0000000 00000012622 13355134677 0021716 0 ustar 00root root 0000000 0000000 """
These are a collection of helper methods that can be
used to save a modbus server context to file for backup,
checkpointing, or any other purpose. There use is very
simple::
context = server.context
saver = JsonDatastoreSaver(context)
saver.save()
These can then be re-opened by the parsers in the
modbus_mapping module. At the moment, the supported
output formats are:
* csv
* json
* xml
To implement your own, simply subclass ModbusDatastoreSaver
and supply the needed callbacks for your given format:
* handle_store_start(self, store)
* handle_store_end(self, store)
* handle_slave_start(self, slave)
* handle_slave_end(self, slave)
* handle_save_start(self)
* handle_save_end(self)
"""
import json
import xml.etree.ElementTree as xml
class ModbusDatastoreSaver(object):
""" An abstract base class that can be used to implement
a persistance format for the modbus server context. In
order to use it, just complete the neccessary callbacks
(SAX style) that your persistance format needs.
"""
def __init__(self, context, path=None):
""" Initialize a new instance of the saver.
:param context: The modbus server context
:param path: The output path to save to
"""
self.context = context
self.path = path or 'modbus-context-dump'
def save(self):
""" The main runner method to save the
context to file which calls the various
callbacks which the sub classes will
implement.
"""
with open(self.path, 'w') as self.file_handle:
self.handle_save_start()
for slave_name, slave in self.context:
self.handle_slave_start(slave_name)
for store_name, store in slave.store.iteritems():
self.handle_store_start(store_name)
self.handle_store_values(iter(store))
self.handle_store_end(store_name)
self.handle_slave_end(slave_name)
self.handle_save_end()
#------------------------------------------------------------
# predefined state machine callbacks
#------------------------------------------------------------
def handle_save_start(self):
pass
def handle_store_start(self, store):
pass
def handle_store_end(self, store):
pass
def handle_slave_start(self, slave):
pass
def handle_slave_end(self, slave):
pass
def handle_save_end(self):
pass
# ---------------------------------------------------------------- #
# Implementations of the data store savers
# ---------------------------------------------------------------- #
class JsonDatastoreSaver(ModbusDatastoreSaver):
""" An implementation of the modbus datastore saver
that persists the context as a json document.
"""
_context = None
_store = None
_slave = None
STORE_NAMES = {
'i': 'input-registers',
'd': 'discretes',
'h': 'holding-registers',
'c': 'coils',
}
def handle_save_start(self):
self._context = dict()
def handle_slave_start(self, slave):
self._context[hex(slave)] = self._slave = dict()
def handle_store_start(self, store):
self._store = self.STORE_NAMES[store]
def handle_store_values(self, values):
self._slave[self._store] = dict(values)
def handle_save_end(self):
json.dump(self._context, self.file_handle)
class CsvDatastoreSaver(ModbusDatastoreSaver):
""" An implementation of the modbus datastore saver
that persists the context as a csv document.
"""
_context = None
_store = None
_line = None
NEWLINE = '\r\n'
HEADER = "slave,store,address,value" + NEWLINE
STORE_NAMES = {
'i': 'i',
'd': 'd',
'h': 'h',
'c': 'c',
}
def handle_save_start(self):
self.file_handle.write(self.HEADER)
def handle_slave_start(self, slave):
self._line = [str(slave)]
def handle_store_start(self, store):
self._line.append(self.STORE_NAMES[store])
def handle_store_values(self, values):
self.file_handle.writelines(self.handle_store_value(values))
def handle_store_end(self, store):
self._line.pop()
def handle_store_value(self, values):
for a, v in values:
yield ','.join(self._line + [str(a), str(v)]) + self.NEWLINE
class XmlDatastoreSaver(ModbusDatastoreSaver):
""" An implementation of the modbus datastore saver
that persists the context as a XML document.
"""
_context = None
_store = None
STORE_NAMES = {
'i' : 'input-registers',
'd' : 'discretes',
'h' : 'holding-registers',
'c' : 'coils',
}
def handle_save_start(self):
self._context = xml.Element("context")
self._root = xml.ElementTree(self._context)
def handle_slave_start(self, slave):
self._slave = xml.SubElement(self._context, "slave")
self._slave.set("id", str(slave))
def handle_store_start(self, store):
self._store = xml.SubElement(self._slave, "store")
self._store.set("function", self.STORE_NAMES[store])
def handle_store_values(self, values):
for address, value in values:
entry = xml.SubElement(self._store, "entry")
entry.text = str(value)
entry.set("address", str(address))
def handle_save_end(self):
self._root.write(self.file_handle)
pymodbus-2.1.0/examples/contrib/modbus_scraper.py 0000775 0000000 0000000 00000024424 13355134677 0022243 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
This is a simple scraper that can be pointed at a
modbus device to pull down all its values and store
them as a collection of sequential data blocks.
"""
import pickle
from optparse import OptionParser
from twisted.internet import serialport, reactor
from twisted.internet.protocol import ClientFactory
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext
from pymodbus.factory import ClientDecoder
from pymodbus.client.async.twisted import ModbusClientProtocol
# -------------------------------------------------------------------------- #
# Configure the client logging
# -------------------------------------------------------------------------- #
import logging
log = logging.getLogger("pymodbus")
# --------------------------------------------------------------------------- #
# Choose the framer you want to use
# --------------------------------------------------------------------------- #
from pymodbus.transaction import ModbusBinaryFramer
from pymodbus.transaction import ModbusAsciiFramer
from pymodbus.transaction import ModbusRtuFramer
from pymodbus.transaction import ModbusSocketFramer
# --------------------------------------------------------------------------- #
# Define some constants
# --------------------------------------------------------------------------- #
COUNT = 8 # The number of bits/registers to read at once
DELAY = 0 # The delay between subsequent reads
SLAVE = 0x01 # The slave unit id to read from
# --------------------------------------------------------------------------- #
# A simple scraper protocol
# --------------------------------------------------------------------------- #
# I tried to spread the load across the device, but feel free to modify the
# logic to suit your own purpose.
# --------------------------------------------------------------------------- #
class ScraperProtocol(ModbusClientProtocol):
address = None
def __init__(self, framer, endpoint):
""" Initializes our custom protocol
:param framer: The decoder to use to process messages
:param endpoint: The endpoint to send results to
"""
ModbusClientProtocol.__init__(self, framer)
self.endpoint = endpoint
def connectionMade(self):
""" Callback for when the client has connected
to the remote server.
"""
super(ScraperProtocol, self).connectionMade()
log.debug("Beginning the processing loop")
self.address = self.factory.starting
reactor.callLater(DELAY, self.scrape_holding_registers)
def connectionLost(self, reason):
""" Callback for when the client disconnects from the
server.
:param reason: The reason for the disconnection
"""
reactor.callLater(DELAY, reactor.stop)
def scrape_holding_registers(self):
""" Defer fetching holding registers
"""
log.debug("reading holding registers: %d" % self.address)
d = self.read_holding_registers(self.address, count=COUNT, unit=SLAVE)
d.addCallbacks(self.scrape_discrete_inputs, self.error_handler)
def scrape_discrete_inputs(self, response):
""" Defer fetching holding registers
"""
log.debug("reading discrete inputs: %d" % self.address)
self.endpoint.write((3, self.address, response.registers))
d = self.read_discrete_inputs(self.address, count=COUNT, unit=SLAVE)
d.addCallbacks(self.scrape_input_registers, self.error_handler)
def scrape_input_registers(self, response):
""" Defer fetching holding registers
"""
log.debug("reading discrete inputs: %d" % self.address)
self.endpoint.write((2, self.address, response.bits))
d = self.read_input_registers(self.address, count=COUNT, unit=SLAVE)
d.addCallbacks(self.scrape_coils, self.error_handler)
def scrape_coils(self, response):
""" Write values of holding registers, defer fetching coils
:param response: The response to process
"""
log.debug("reading coils: %d" % self.address)
self.endpoint.write((4, self.address, response.registers))
d = self.read_coils(self.address, count=COUNT, unit=SLAVE)
d.addCallbacks(self.start_next_cycle, self.error_handler)
def start_next_cycle(self, response):
""" Write values of coils, trigger next cycle
:param response: The response to process
"""
log.debug("starting next round: %d" % self.address)
self.endpoint.write((1, self.address, response.bits))
self.address += COUNT
if self.address >= self.factory.ending:
self.endpoint.finalize()
self.transport.loseConnection()
else:
reactor.callLater(DELAY, self.scrape_holding_registers)
def error_handler(self, failure):
""" Handle any twisted errors
:param failure: The error to handle
"""
log.error(failure)
# --------------------------------------------------------------------------- #
# a factory for the example protocol
# --------------------------------------------------------------------------- #
# This is used to build client protocol's if you tie into twisted's method
# of processing. It basically produces client instances of the underlying
# protocol::
#
# Factory(Protocol) -> ProtocolInstance
#
# It also persists data between client instances (think protocol singelton).
# --------------------------------------------------------------------------- #
class ScraperFactory(ClientFactory):
protocol = ScraperProtocol
def __init__(self, framer, endpoint, query):
""" Remember things necessary for building a protocols """
self.framer = framer
self.endpoint = endpoint
self.starting, self.ending = query
def buildProtocol(self, _):
""" Create a protocol and start the reading cycle """
protocol = self.protocol(self.framer, self.endpoint)
protocol.factory = self
return protocol
# --------------------------------------------------------------------------- #
# a custom client for our device
# --------------------------------------------------------------------------- #
# Twisted provides a number of helper methods for creating and starting
# clients:
# - protocol.ClientCreator
# - reactor.connectTCP
#
# How you start your client is really up to you.
# --------------------------------------------------------------------------- #
class SerialModbusClient(serialport.SerialPort):
def __init__(self, factory, *args, **kwargs):
""" Setup the client and start listening on the serial port
:param factory: The factory to build clients with
"""
protocol = factory.buildProtocol(None)
self.decoder = ClientDecoder()
serialport.SerialPort.__init__(self, protocol, *args, **kwargs)
# --------------------------------------------------------------------------- #
# a custom endpoint for our results
# --------------------------------------------------------------------------- #
# An example line reader, this can replace with:
# - the TCP protocol
# - a context recorder
# - a database or file recorder
# --------------------------------------------------------------------------- #
class LoggingContextReader(object):
def __init__(self, output):
""" Initialize a new instance of the logger
:param output: The output file to save to
"""
self.output = output
self.context = ModbusSlaveContext(
di = ModbusSequentialDataBlock.create(),
co = ModbusSequentialDataBlock.create(),
hr = ModbusSequentialDataBlock.create(),
ir = ModbusSequentialDataBlock.create())
def write(self, response):
""" Handle the next modbus response
:param response: The response to process
"""
log.info("Read Data: %s" % str(response))
fx, address, values = response
self.context.setValues(fx, address, values)
def finalize(self):
with open(self.output, "w") as handle:
pickle.dump(self.context, handle)
# -------------------------------------------------------------------------- #
# Main start point
# -------------------------------------------------------------------------- #
def get_options():
""" A helper method to parse the command line options
:returns: The options manager
"""
parser = OptionParser()
parser.add_option("-o", "--output",
help="The resulting output file for the scrape",
dest="output", default="datastore.pickle")
parser.add_option("-p", "--port",
help="The port to connect to", type='int',
dest="port", default=502)
parser.add_option("-s", "--server",
help="The server to scrape",
dest="host", default="127.0.0.1")
parser.add_option("-r", "--range",
help="The address range to scan",
dest="query", default="0:1000")
parser.add_option("-d", "--debug",
help="Enable debug tracing",
action="store_true", dest="debug", default=False)
(opt, arg) = parser.parse_args()
return opt
def main():
""" The main runner function """
options = get_options()
if options.debug:
try:
log.setLevel(logging.DEBUG)
logging.basicConfig()
except Exception as ex:
print("Logging is not supported on this system")
# split the query into a starting and ending range
query = [int(p) for p in options.query.split(':')]
try:
log.debug("Initializing the client")
framer = ModbusSocketFramer(ClientDecoder())
reader = LoggingContextReader(options.output)
factory = ScraperFactory(framer, reader, query)
# how to connect based on TCP vs Serial clients
if isinstance(framer, ModbusSocketFramer):
reactor.connectTCP(options.host, options.port, factory)
else:
SerialModbusClient(factory, options.port, reactor)
log.debug("Starting the client")
reactor.run()
log.debug("Finished scraping the client")
except Exception as ex:
print(ex)
# --------------------------------------------------------------------------- #
# Main jumper
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
main()
pymodbus-2.1.0/examples/contrib/modbus_simulator.py 0000664 0000000 0000000 00000010474 13355134677 0022620 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
An example of creating a fully implemented modbus server
with read/write data as well as user configurable base data
"""
import pickle
from optparse import OptionParser
from twisted.internet import reactor
from pymodbus.server.async import StartTcpServer
from pymodbus.datastore import ModbusServerContext,ModbusSlaveContext
# -------------------------------------------------------------------------- #
# Logging
# -------------------------------------------------------------------------- #
import logging
logging.basicConfig()
server_log = logging.getLogger("pymodbus.server")
protocol_log = logging.getLogger("pymodbus.protocol")
# -------------------------------------------------------------------------- #
# Extra Global Functions
# -------------------------------------------------------------------------- #
# These are extra helper functions that don't belong in a class
# -------------------------------------------------------------------------- #
import getpass
def root_test():
""" Simple test to see if we are running as root """
return True # removed for the time being as it isn't portable
#return getpass.getuser() == "root"
# -------------------------------------------------------------------------- #
# Helper Classes
# -------------------------------------------------------------------------- #
class ConfigurationException(Exception):
""" Exception for configuration error """
def __init__(self, string):
""" Initializes the ConfigurationException instance
:param string: The message to append to the exception
"""
Exception.__init__(self, string)
self.string = string
def __str__(self):
""" Builds a representation of the object
:returns: A string representation of the object
"""
return 'Configuration Error: %s' % self.string
class Configuration:
"""
Class used to parse configuration file and create and modbus
datastore.
The format of the configuration file is actually just a
python pickle, which is a compressed memory dump from
the scraper.
"""
def __init__(self, config):
"""
Trys to load a configuration file, lets the file not
found exception fall through
:param config: The pickled datastore
"""
try:
self.file = open(config, "rb")
except Exception as e:
_logger.critical(str(e))
raise ConfigurationException("File not found %s" % config)
def parse(self):
""" Parses the config file and creates a server context
"""
handle = pickle.load(self.file)
try: # test for existance, or bomb
dsd = handle['di']
csd = handle['ci']
hsd = handle['hr']
isd = handle['ir']
except Exception:
raise ConfigurationException("Invalid Configuration")
slave = ModbusSlaveContext(d=dsd, c=csd, h=hsd, i=isd)
return ModbusServerContext(slaves=slave)
# -------------------------------------------------------------------------- #
# Main start point
# -------------------------------------------------------------------------- #
def main():
""" Server launcher """
parser = OptionParser()
parser.add_option("-c", "--conf",
help="The configuration file to load",
dest="file")
parser.add_option("-D", "--debug",
help="Turn on to enable tracing",
action="store_true", dest="debug", default=False)
(opt, arg) = parser.parse_args()
# enable debugging information
if opt.debug:
try:
server_log.setLevel(logging.DEBUG)
protocol_log.setLevel(logging.DEBUG)
except Exception as e:
print("Logging is not supported on this system")
# parse configuration file and run
try:
conf = Configuration(opt.file)
StartTcpServer(context=conf.parse())
except ConfigurationException as err:
print(err)
parser.print_help()
# -------------------------------------------------------------------------- #
# Main jumper
# -------------------------------------------------------------------------- #
if __name__ == "__main__":
if root_test():
main()
else:
print("This script must be run as root!")
pymodbus-2.1.0/examples/contrib/modicon_payload.py 0000664 0000000 0000000 00000022004 13355134677 0022361 0 ustar 00root root 0000000 0000000 """
Modbus Modicon Payload Builder
-----------------------------------------------------------
This is an example of building a custom payload builder
that can be used in the pymodbus library. Below is a
simple modicon encoded builder and decoder.
"""
from struct import pack, unpack
from pymodbus.constants import Endian
from pymodbus.interfaces import IPayloadBuilder
from pymodbus.utilities import pack_bitstring
from pymodbus.utilities import unpack_bitstring
from pymodbus.exceptions import ParameterException
class ModiconPayloadBuilder(IPayloadBuilder):
"""
A utility that helps build modicon encoded payload
messages to be written with the various modbus messages.
example::
builder = ModiconPayloadBuilder()
builder.add_8bit_uint(1)
builder.add_16bit_uint(2)
payload = builder.build()
"""
def __init__(self, payload=None, endian=Endian.Little):
""" Initialize a new instance of the payload builder
:param payload: Raw payload data to initialize with
:param endian: The endianess of the payload
"""
self._payload = payload or []
self._endian = endian
def __str__(self):
""" Return the payload buffer as a string
:returns: The payload buffer as a string
"""
return ''.join(self._payload)
def reset(self):
""" Reset the payload buffer
"""
self._payload = []
def build(self):
""" Return the payload buffer as a list
This list is two bytes per element and can
thus be treated as a list of registers.
:returns: The payload buffer as a list
"""
string = str(self)
length = len(string)
string = string + ('\x00' * (length % 2))
return [string[i:i+2] for i in range(0, length, 2)]
def add_bits(self, values):
""" Adds a collection of bits to be encoded
If these are less than a multiple of eight,
they will be left padded with 0 bits to make
it so.
:param values: The value to add to the buffer
"""
value = pack_bitstring(values)
self._payload.append(value)
def add_8bit_uint(self, value):
""" Adds a 8 bit unsigned int to the buffer
:param value: The value to add to the buffer
"""
fstring = self._endian + 'B'
self._payload.append(pack(fstring, value))
def add_16bit_uint(self, value):
""" Adds a 16 bit unsigned int to the buffer
:param value: The value to add to the buffer
"""
fstring = self._endian + 'H'
self._payload.append(pack(fstring, value))
def add_32bit_uint(self, value):
""" Adds a 32 bit unsigned int to the buffer
:param value: The value to add to the buffer
"""
fstring = self._endian + 'I'
handle = pack(fstring, value)
handle = handle[2:] + handle[:2]
self._payload.append(handle)
def add_8bit_int(self, value):
""" Adds a 8 bit signed int to the buffer
:param value: The value to add to the buffer
"""
fstring = self._endian + 'b'
self._payload.append(pack(fstring, value))
def add_16bit_int(self, value):
""" Adds a 16 bit signed int to the buffer
:param value: The value to add to the buffer
"""
fstring = self._endian + 'h'
self._payload.append(pack(fstring, value))
def add_32bit_int(self, value):
""" Adds a 32 bit signed int to the buffer
:param value: The value to add to the buffer
"""
fstring = self._endian + 'i'
handle = pack(fstring, value)
handle = handle[2:] + handle[:2]
self._payload.append(handle)
def add_32bit_float(self, value):
""" Adds a 32 bit float to the buffer
:param value: The value to add to the buffer
"""
fstring = self._endian + 'f'
handle = pack(fstring, value)
handle = handle[2:] + handle[:2]
self._payload.append(handle)
def add_string(self, value):
""" Adds a string to the buffer
:param value: The value to add to the buffer
"""
fstring = self._endian + 's'
for c in value:
self._payload.append(pack(fstring, c))
class ModiconPayloadDecoder(object):
"""
A utility that helps decode modicon encoded payload
messages from a modbus reponse message. What follows is
a simple example::
decoder = ModiconPayloadDecoder(payload)
first = decoder.decode_8bit_uint()
second = decoder.decode_16bit_uint()
"""
def __init__(self, payload, endian):
""" Initialize a new payload decoder
:param payload: The payload to decode with
"""
self._payload = payload
self._pointer = 0x00
self._endian = endian
@staticmethod
def from_registers(registers, endian=Endian.Little):
""" Initialize a payload decoder with the result of
reading a collection of registers from a modbus device.
The registers are treated as a list of 2 byte values.
We have to do this because of how the data has already
been decoded by the rest of the library.
:param registers: The register results to initialize with
:param endian: The endianess of the payload
:returns: An initialized PayloadDecoder
"""
if isinstance(registers, list): # repack into flat binary
payload = ''.join(pack('>H', x) for x in registers)
return ModiconPayloadDecoder(payload, endian)
raise ParameterException('Invalid collection of registers supplied')
@staticmethod
def from_coils(coils, endian=Endian.Little):
""" Initialize a payload decoder with the result of
reading a collection of coils from a modbus device.
The coils are treated as a list of bit(boolean) values.
:param coils: The coil results to initialize with
:param endian: The endianess of the payload
:returns: An initialized PayloadDecoder
"""
if isinstance(coils, list):
payload = pack_bitstring(coils)
return ModiconPayloadDecoder(payload, endian)
raise ParameterException('Invalid collection of coils supplied')
def reset(self):
""" Reset the decoder pointer back to the start
"""
self._pointer = 0x00
def decode_8bit_uint(self):
""" Decodes a 8 bit unsigned int from the buffer
"""
self._pointer += 1
fstring = self._endian + 'B'
handle = self._payload[self._pointer - 1:self._pointer]
return unpack(fstring, handle)[0]
def decode_16bit_uint(self):
""" Decodes a 16 bit unsigned int from the buffer
"""
self._pointer += 2
fstring = self._endian + 'H'
handle = self._payload[self._pointer - 2:self._pointer]
return unpack(fstring, handle)[0]
def decode_32bit_uint(self):
""" Decodes a 32 bit unsigned int from the buffer
"""
self._pointer += 4
fstring = self._endian + 'I'
handle = self._payload[self._pointer - 4:self._pointer]
handle = handle[2:] + handle[:2]
return unpack(fstring, handle)[0]
def decode_8bit_int(self):
""" Decodes a 8 bit signed int from the buffer
"""
self._pointer += 1
fstring = self._endian + 'b'
handle = self._payload[self._pointer - 1:self._pointer]
return unpack(fstring, handle)[0]
def decode_16bit_int(self):
""" Decodes a 16 bit signed int from the buffer
"""
self._pointer += 2
fstring = self._endian + 'h'
handle = self._payload[self._pointer - 2:self._pointer]
return unpack(fstring, handle)[0]
def decode_32bit_int(self):
""" Decodes a 32 bit signed int from the buffer
"""
self._pointer += 4
fstring = self._endian + 'i'
handle = self._payload[self._pointer - 4:self._pointer]
handle = handle[2:] + handle[:2]
return unpack(fstring, handle)[0]
def decode_32bit_float(self, size=1):
""" Decodes a float from the buffer
"""
self._pointer += 4
fstring = self._endian + 'f'
handle = self._payload[self._pointer - 4:self._pointer]
handle = handle[2:] + handle[:2]
return unpack(fstring, handle)[0]
def decode_bits(self):
""" Decodes a byte worth of bits from the buffer
"""
self._pointer += 1
handle = self._payload[self._pointer - 1:self._pointer]
return unpack_bitstring(handle)
def decode_string(self, size=1):
""" Decodes a string from the buffer
:param size: The size of the string to decode
"""
self._pointer += size
return self._payload[self._pointer - size:self._pointer]
# -------------------------------------------------------------------------- #
# Exported Identifiers
# -------------------------------------------------------------------------- #
__all__ = ["ModiconPayloadBuilder", "ModiconPayloadDecoder"]
pymodbus-2.1.0/examples/contrib/remote_server_context.py 0000664 0000000 0000000 00000017306 13355134677 0023656 0 ustar 00root root 0000000 0000000 """
Although there is a remote server context already in the main library,
it works under the assumption that users would have a server context
of the following form::
server_context = {
0x00: client('host1.something.com'),
0x01: client('host2.something.com'),
0x02: client('host3.something.com')
}
This example is how to create a server context where the client is
pointing to the same host, but the requested slave id is used as the
slave for the client::
server_context = {
0x00: client('host1.something.com', 0x00),
0x01: client('host1.something.com', 0x01),
0x02: client('host1.something.com', 0x02)
}
"""
from pymodbus.exceptions import NotImplementedException
from pymodbus.interfaces import IModbusSlaveContext
# -------------------------------------------------------------------------- #
# Logging
# -------------------------------------------------------------------------- #
import logging
_logger = logging.getLogger(__name__)
# -------------------------------------------------------------------------- #
# Slave Context
# -------------------------------------------------------------------------- #
# Basically we create a new slave context for the given slave identifier so
# that this slave context will only make requests to that slave with the
# client that the server is maintaining.
# -------------------------------------------------------------------------- #
class RemoteSingleSlaveContext(IModbusSlaveContext):
""" This is a remote server context that allows one
to create a server context backed by a single client that
may be attached to many slave units. This can be used to
effectively create a modbus forwarding server.
"""
def __init__(self, context, unit_id):
""" Initializes the datastores
:param context: The underlying context to operate with
:param unit_id: The slave that this context will contact
"""
self.context = context
self.unit_id = unit_id
def reset(self):
""" Resets all the datastores to their default values """
raise NotImplementedException()
def validate(self, fx, address, count=1):
""" Validates the request to make sure it is in range
:param fx: The function we are working with
:param address: The starting address
:param count: The number of values to test
:returns: True if the request in within range, False otherwise
"""
_logger.debug("validate[%d] %d:%d" % (fx, address, count))
result = self.context.get_callbacks[self.decode(fx)](address,
count,
self.unit_id)
return not result.isError()
def getValues(self, fx, address, count=1):
""" Get `count` values from datastore
:param fx: The function we are working with
:param address: The starting address
:param count: The number of values to retrieve
:returns: The requested values from a:a+c
"""
_logger.debug("get values[%d] %d:%d" % (fx, address, count))
result = self.context.get_callbacks[self.decode(fx)](address,
count,
self.unit_id)
return self.__extract_result(self.decode(fx), result)
def setValues(self, fx, address, values):
""" Sets the datastore with the supplied values
:param fx: The function we are working with
:param address: The starting address
:param values: The new values to be set
"""
_logger.debug("set values[%d] %d:%d" % (fx, address, len(values)))
self.context.set_callbacks[self.decode(fx)](address,
values,
self.unit_id)
def __str__(self):
""" Returns a string representation of the context
:returns: A string representation of the context
"""
return "Remote Single Slave Context(%s)" % self.unit_id
def __extract_result(self, fx, result):
""" A helper method to extract the values out of
a response. The future api should make the result
consistent so we can just call `result.getValues()`.
:param fx: The function to call
:param result: The resulting data
"""
if not result.isError():
if fx in ['d', 'c']:
return result.bits
if fx in ['h', 'i']:
return result.registers
else:
return result
# -------------------------------------------------------------------------- #
# Server Context
# -------------------------------------------------------------------------- #
# Think of this as simply a dictionary of { unit_id: client(req, unit_id) }
# -------------------------------------------------------------------------- #
class RemoteServerContext(object):
""" This is a remote server context that allows one
to create a server context backed by a single client that
may be attached to many slave units. This can be used to
effectively create a modbus forwarding server.
"""
def __init__(self, client):
""" Initializes the datastores
:param client: The client to retrieve values with
"""
self.get_callbacks = {
'd': lambda a, c, s: client.read_discrete_inputs(a, c, s),
'c': lambda a, c, s: client.read_coils(a, c, s),
'h': lambda a, c, s: client.read_holding_registers(a, c, s),
'i': lambda a, c, s: client.read_input_registers(a, c, s),
}
self.set_callbacks = {
'd': lambda a, v, s: client.write_coils(a, v, s),
'c': lambda a, v, s: client.write_coils(a, v, s),
'h': lambda a, v, s: client.write_registers(a, v, s),
'i': lambda a, v, s: client.write_registers(a, v, s),
}
self._client = client
self.slaves = {} # simply a cache
def __str__(self):
""" Returns a string representation of the context
:returns: A string representation of the context
"""
return "Remote Server Context(%s)" % self._client
def __iter__(self):
""" Iterater over the current collection of slave
contexts.
:returns: An iterator over the slave contexts
"""
# note, this may not include all slaves
return iter(self.slaves.items())
def __contains__(self, slave):
""" Check if the given slave is in this list
:param slave: slave The slave to check for existance
:returns: True if the slave exists, False otherwise
"""
# we don't want to check the cache here as the
# slave may not exist yet or may not exist any
# more. The best thing to do is try and fail.
return True
def __setitem__(self, slave, context):
""" Used to set a new slave context
:param slave: The slave context to set
:param context: The new context to set for this slave
"""
raise NotImplementedException() # doesn't make sense here
def __delitem__(self, slave):
""" Wrapper used to access the slave context
:param slave: The slave context to remove
"""
raise NotImplementedException() # doesn't make sense here
def __getitem__(self, slave):
""" Used to get access to a slave context
:param slave: The slave context to get
:returns: The requested slave context
"""
if slave not in self.slaves:
self.slaves[slave] = RemoteSingleSlaveContext(self, slave)
return self.slaves[slave]
pymodbus-2.1.0/examples/contrib/requirements.txt 0000664 0000000 0000000 00000000350 13355134677 0022132 0 ustar 00root root 0000000 0000000 # -------------------------------------------------------------------
# if you want to use the custom data stores, uncomment these
# -------------------------------------------------------------------
SQLAlchemy==0.7.9
redis==2.6.2
pymodbus-2.1.0/examples/contrib/rx_messages 0000664 0000000 0000000 00000014215 13355134677 0021116 0 ustar 00root root 0000000 0000000 # ------------------------------------------------------------
# What follows is a collection of encoded messages that can
# be used to test the message-parser. Simply uncomment the
# messages you want decoded and run the message parser with
# the given arguments. What follows is the listing of messages
# that are encoded in each format:
#
# - ReadHoldingRegistersResponse
# - ReadDiscreteInputsResponse
# - ReadInputRegistersResponse
# - ReadCoilsResponse
# - WriteMultipleCoilsResponse
# - WriteMultipleRegistersResponse
# - WriteSingleRegisterResponse
# - WriteSingleCoilResponse
# - ReadWriteMultipleRegistersResponse
# - ReadExceptionStatusResponse
# - GetCommEventCounterResponse
# - GetCommEventLogResponse
# - ReportSlaveIdResponse
# - ReadFileRecordResponse
# - WriteFileRecordResponse
# - MaskWriteRegisterResponse
# - ReadFifoQueueResponse
# - ReadDeviceInformationResponse
# - ReturnQueryDataResponse
# - RestartCommunicationsOptionResponse
# - ReturnDiagnosticRegisterResponse
# - ChangeAsciiInputDelimiterResponse
# - ForceListenOnlyModeResponse
# - ClearCountersResponse
# - ReturnBusMessageCountResponse
# - ReturnBusCommunicationErrorCountResponse
# - ReturnBusExceptionErrorCountResponse
# - ReturnSlaveMessageCountResponse
# - ReturnSlaveNoReponseCountResponse
# - ReturnSlaveNAKCountResponse
# - ReturnSlaveBusyCountResponse
# - ReturnSlaveBusCharacterOverrunCountResponse
# - ReturnIopOverrunCountResponse
# - ClearOverrunCountResponse
# - GetClearModbusPlusResponse
# ------------------------------------------------------------
# Modbus TCP Messages
# ------------------------------------------------------------
# [ MBAP Header ] [ Function Code] [ Data ]
# [ tid ][ pid ][ length ][ uid ]
# 2b 2b 2b 1b 1b Nb
#
# ./message-parser -b -p tcp -f messages
# ------------------------------------------------------------
#00010000001301031000010001000100010001000100010001
#000100000004010201ff
#00010000001301041000010001000100010001000100010001
#000100000004010101ff
#000100000006010f00120008
#000100000006011000120008
#000100000006010600120001
#00010000000601050012ff00
#00010000001301171000010001000100010001000100010001
#000100000003010700
#000100000006010b00000008
#000100000009010c06000000000000
#00010000000501110300ff
#000100000003011400
#000100000003011500
#00010000000801160012ffff0000
#00010000001601180012001000010001000100010001000100010001
#000100000008012b0e0183000000
#000100000006010800000000
#000100000006010800010000
#000100000006010800020000
#000100000006010800030000
#00010000000401080004
#0001000000060108000a0000
#0001000000060108000b0000
#0001000000060108000c0000
#0001000000060108000d0000
#0001000000060108000e0000
#0001000000060108000f0000
#000100000006010800100000
#000100000006010800110000
#000100000006010800120000
#000100000006010800130000
#000100000006010800140000
#000100000006010800150000
# ------------------------------------------------------------
# Modbus RTU Messages
# ------------------------------------------------------------
# [Address ][ Function Code] [ Data ][ CRC ]
# 1b 1b Nb 2b
#
# ./message-parser -b -p rtu -f messages
# ------------------------------------------------------------
#0103100001000100010001000100010001000193b4
#010201ffe1c8
#0104100001000100010001000100010001000122c1
#010101ff11c8
#010f00120008f408
#01100012000861ca
#010600120001e80f
#01050012ff002c3f
#01171000010001000100010001000100010001d640
#0107002230
#010b00000008a5cd
#010c060000000000006135
#01110300ffacbc
#0114002f00
#0115002e90
#01160012ffff00004e21
#01180012001000010001000100010001000100010001d74d
#012b0e01830000000faf
#010800000000e00b
#010800010000b1cb
#01080002000041cb
#010800030000100b
#0108000481d9
#0108000a0000c009
#0108000b000091c9
#0108000c00002008
#0108000d000071c8
#0108000e000081c8
#0108000f0000d008
#010800100000e1ce
#010800110000b00e
#010800120000400e
#01080013000011ce
#010800140000a00f
#010800150000f1cf
# ------------------------------------------------------------
# Modbus ASCII Messages
# ------------------------------------------------------------
# [ Start ][Address ][ Function ][ Data ][ LRC ][ End ]
# 1c 2c 2c Nc 2c 2c
#
# ./message-parser -a -p ascii -f messages
# ------------------------------------------------------------
#:01031000010001000100010001000100010001E4
#:010201FFFD
#:01041000010001000100010001000100010001E3
#:010101FFFE
#:010F00120008D6
#:011000120008D5
#:010600120001E6
#:01050012FF00E9
#:01171000010001000100010001000100010001D0
#:010700F8
#:010B00000008EC
#:010C06000000000000ED
#:01110300FFEC
#:011400EB
#:011500EA
#:01160012FFFF0000D9
#:01180012001000010001000100010001000100010001BD
#:012B0E018300000042
#:010800000000F7
#:010800010000F6
#:010800020000F5
#:010800030000F4
#:01080004F3
#:0108000A0000ED
#:0108000B0000EC
#:0108000C0000EB
#:0108000D0000EA
#:0108000E0000E9
#:0108000F0000E8
#:010800100000E7
#:010800110000E6
#:010800120000E5
#:010800130000E4
#:010800140000E3
#:010800150000E2
# ------------------------------------------------------------
# Modbus Binary Messages
# ------------------------------------------------------------
# [ Start ][Address ][ Function ][ Data ][ CRC ][ End ]
# 1b 1b 1b Nb 2b 1b
#
# ./message-parser -b -p binary -f messages
# ------------------------------------------------------------
#7b0103100001000100010001000100010001000193b47d
#7b010201ffe1c87d
#7b0104100001000100010001000100010001000122c17d
#7b010101ff11c87d
#7b010f00120008f4087d
#7b01100012000861ca7d
#7b010600120001e80f7d
#7b01050012ff002c3f7d
#7b01171000010001000100010001000100010001d6407d
#7b01070022307d
#7b010b00000008a5cd7d
#7b010c0600000000000061357d
#7b01110300ffacbc7d
#7b0114002f007d
#7b0115002e907d
#7b01160012ffff00004e217d
#7b01180012001000010001000100010001000100010001d74d7d
#7b012b0e01830000000faf7d
#7b010800000000e00b7d
#7b010800010000b1cb7d
#7b01080002000041cb7d
#7b010800030000100b7d
#7b0108000481d97d
#7b0108000a0000c0097d
#7b0108000b000091c97d
#7b0108000c000020087d
#7b0108000d000071c87d
#7b0108000e000081c87d
#7b0108000f0000d0087d
#7b010800100000e1ce7d
#7b010800110000b00e7d
#7b010800120000400e7d
#7b01080013000011ce7d
#7b010800140000a00f7d
#7b010800150000f1cf7d
pymodbus-2.1.0/examples/contrib/serial_forwarder.py 0000775 0000000 0000000 00000003406 13355134677 0022562 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
"""
Pymodbus Synchronous Serial Forwarder
--------------------------------------------------------------------------
We basically set the context for the tcp serial server to be that of a
serial client! This is just an example of how clever you can be with
the data context (basically anything can become a modbus device).
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.server.sync import StartTcpServer as StartServer
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from pymodbus.datastore.remote import RemoteSlaveContext
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_serial_forwarder():
# ----------------------------------------------------------------------- #
# initialize the datastore(serial client)
# ----------------------------------------------------------------------- #
client = ModbusClient(method='rtu', port='/dev/ptyp0')
store = RemoteSlaveContext(client)
context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
StartServer(context, address=("localhost", 5020))
if __name__ == "__main__":
run_serial_forwarder()
pymodbus-2.1.0/examples/contrib/sunspec_client.py 0000664 0000000 0000000 00000025431 13355134677 0022245 0 ustar 00root root 0000000 0000000 from pymodbus.constants import Endian
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.payload import BinaryPayloadDecoder
from twisted.internet.defer import Deferred
# --------------------------------------------------------------------------- #
# Logging
# --------------------------------------------------------------------------- #
import logging
_logger = logging.getLogger(__name__)
_logger.setLevel(logging.DEBUG)
logging.basicConfig()
# --------------------------------------------------------------------------- #
# Sunspec Common Constants
# --------------------------------------------------------------------------- #
class SunspecDefaultValue(object):
""" A collection of constants to indicate if
a value is not implemented.
"""
Signed16 = 0x8000
Unsigned16 = 0xffff
Accumulator16 = 0x0000
Scale = 0x8000
Signed32 = 0x80000000
Float32 = 0x7fc00000
Unsigned32 = 0xffffffff
Accumulator32 = 0x00000000
Signed64 = 0x8000000000000000
Unsigned64 = 0xffffffffffffffff
Accumulator64 = 0x0000000000000000
String = '\x00'
class SunspecStatus(object):
""" Indicators of the current status of a
sunspec device
"""
Normal = 0x00000000
Error = 0xfffffffe
Unknown = 0xffffffff
class SunspecIdentifier(object):
""" Assigned identifiers that are pre-assigned
by the sunspec protocol.
"""
Sunspec = 0x53756e53
class SunspecModel(object):
""" Assigned device indentifiers that are pre-assigned
by the sunspec protocol.
"""
#---------------------------------------------
# 0xx Common Models
#---------------------------------------------
CommonBlock = 1
AggregatorBlock = 2
#---------------------------------------------
# 1xx Inverter Models
#---------------------------------------------
SinglePhaseIntegerInverter = 101
SplitPhaseIntegerInverter = 102
ThreePhaseIntegerInverter = 103
SinglePhaseFloatsInverter = 103
SplitPhaseFloatsInverter = 102
ThreePhaseFloatsInverter = 103
#---------------------------------------------
# 2xx Meter Models
#---------------------------------------------
SinglePhaseMeter = 201
SplitPhaseMeter = 201
WyeConnectMeter = 201
DeltaConnectMeter = 201
#---------------------------------------------
# 3xx Environmental Models
#---------------------------------------------
BaseMeteorological = 301
Irradiance = 302
BackOfModuleTemperature = 303
Inclinometer = 304
Location = 305
ReferencePoint = 306
BaseMeteorological = 307
MiniMeteorological = 308
#---------------------------------------------
# 4xx String Combiner Models
#---------------------------------------------
BasicStringCombiner = 401
AdvancedStringCombiner = 402
#---------------------------------------------
# 5xx Panel Models
#---------------------------------------------
PanelFloat = 501
PanelInteger = 502
#---------------------------------------------
# 641xx Outback Blocks
#---------------------------------------------
OutbackDeviceIdentifier = 64110
OutbackChargeController = 64111
OutbackFMSeriesChargeController = 64112
OutbackFXInverterRealTime = 64113
OutbackFXInverterConfiguration = 64114
OutbackSplitPhaseRadianInverter = 64115
OutbackRadianInverterConfiguration = 64116
OutbackSinglePhaseRadianInverterRealTime = 64117
OutbackFLEXNetDCRealTime = 64118
OutbackFLEXNetDCConfiguration = 64119
OutbackSystemControl = 64120
#---------------------------------------------
# 64xxx Vender Extension Block
#---------------------------------------------
EndOfSunSpecMap = 65535
@classmethod
def lookup(klass, code):
""" Given a device identifier, return the
device model name for that identifier
:param code: The device code to lookup
:returns: The device model name, or None if none available
"""
values = dict((v, k) for k, v in klass.__dict__.iteritems()
if not callable(v))
return values.get(code, None)
class SunspecOffsets(object):
""" Well known offsets that are used throughout
the sunspec protocol
"""
CommonBlock = 40000
CommonBlockLength = 69
AlternateCommonBlock = 50000
# --------------------------------------------------------------------------- #
# Common Functions
# --------------------------------------------------------------------------- #
def defer_or_apply(func):
""" Decorator to apply an adapter method
to a result regardless if it is a deferred
or a concrete response.
:param func: The function to decorate
"""
def closure(future, adapt):
if isinstance(future, Deferred):
d = Deferred()
future.addCallback(lambda r: d.callback(adapt(r)))
return d
return adapt(future)
return closure
def create_sunspec_sync_client(host):
""" A quick helper method to create a sunspec
client.
:param host: The host to connect to
:returns: an initialized SunspecClient
"""
modbus = ModbusTcpClient(host)
modbus.connect()
client = SunspecClient(modbus)
client.initialize()
return client
# --------------------------------------------------------------------------- #
# Sunspec Client
# --------------------------------------------------------------------------- #
class SunspecDecoder(BinaryPayloadDecoder):
""" A decoder that deals correctly with the sunspec
binary format.
"""
def __init__(self, payload, byteorder):
""" Initialize a new instance of the SunspecDecoder
.. note:: This is always set to big endian byte order
as specified in the protocol.
"""
byteorder = Endian.Big
BinaryPayloadDecoder.__init__(self, payload, byteorder)
def decode_string(self, size=1):
""" Decodes a string from the buffer
:param size: The size of the string to decode
"""
self._pointer += size
string = self._payload[self._pointer - size:self._pointer]
return string.split(SunspecDefaultValue.String)[0]
class SunspecClient(object):
def __init__(self, client):
""" Initialize a new instance of the client
:param client: The modbus client to use
"""
self.client = client
self.offset = SunspecOffsets.CommonBlock
def initialize(self):
""" Initialize the underlying client values
:returns: True if successful, false otherwise
"""
decoder = self.get_device_block(self.offset, 2)
if decoder.decode_32bit_uint() == SunspecIdentifier.Sunspec:
return True
self.offset = SunspecOffsets.AlternateCommonBlock
decoder = self.get_device_block(self.offset, 2)
return decoder.decode_32bit_uint() == SunspecIdentifier.Sunspec
def get_common_block(self):
""" Read and return the sunspec common information
block.
:returns: A dictionary of the common block information
"""
length = SunspecOffsets.CommonBlockLength
decoder = self.get_device_block(self.offset, length)
return {
'SunSpec_ID': decoder.decode_32bit_uint(),
'SunSpec_DID': decoder.decode_16bit_uint(),
'SunSpec_Length': decoder.decode_16bit_uint(),
'Manufacturer': decoder.decode_string(size=32),
'Model': decoder.decode_string(size=32),
'Options': decoder.decode_string(size=16),
'Version': decoder.decode_string(size=16),
'SerialNumber': decoder.decode_string(size=32),
'DeviceAddress': decoder.decode_16bit_uint(),
'Next_DID': decoder.decode_16bit_uint(),
'Next_DID_Length': decoder.decode_16bit_uint(),
}
def get_device_block(self, offset, size):
""" A helper method to retrieve the next device block
.. note:: We will read 2 more registers so that we have
the information for the next block.
:param offset: The offset to start reading at
:param size: The size of the offset to read
:returns: An initialized decoder for that result
"""
_logger.debug("reading device block[{}..{}]".format(offset, offset + size))
response = self.client.read_holding_registers(offset, size + 2)
return SunspecDecoder.fromRegisters(response.registers)
def get_all_device_blocks(self):
""" Retrieve all the available blocks in the supplied
sunspec device.
.. note:: Since we do not know how to decode the available
blocks, this returns a list of dictionaries of the form:
decoder: the-binary-decoder,
model: the-model-identifier (name)
:returns: A list of the available blocks
"""
blocks = []
offset = self.offset + 2
model = SunspecModel.CommonBlock
while model != SunspecModel.EndOfSunSpecMap:
decoder = self.get_device_block(offset, 2)
model = decoder.decode_16bit_uint()
length = decoder.decode_16bit_uint()
blocks.append({
'model' : model,
'name' : SunspecModel.lookup(model),
'length': length,
'offset': offset + length + 2
})
offset += length + 2
return blocks
#------------------------------------------------------------
# A quick test runner
#------------------------------------------------------------
if __name__ == "__main__":
client = create_sunspec_sync_client("YOUR.HOST.GOES.HERE")
# print out all the device common block
common = client.get_common_block()
for key, value in common.iteritems():
if key == "SunSpec_DID":
value = SunspecModel.lookup(value)
print("{:<20}: {}".format(key, value))
# print out all the available device blocks
blocks = client.get_all_device_blocks()
for block in blocks:
print(block)
client.client.close()
pymodbus-2.1.0/examples/contrib/thread_safe_datastore.py 0000664 0000000 0000000 00000017746 13355134677 0023554 0 ustar 00root root 0000000 0000000 import threading
from contextlib import contextmanager
from pymodbus.datastore.store import BaseModbusDataBlock
class ContextWrapper(object):
""" This is a simple wrapper around enter
and exit functions that conforms to the pyhton
context manager protocol:
with ContextWrapper(enter, leave):
do_something()
"""
def __init__(self, enter=None, leave=None, factory=None):
self._enter = enter
self._leave = leave
self._factory = factory
def __enter__(self):
if self.enter: self._enter()
return self if not self._factory else self._factory()
def __exit__(self, args):
if self._leave: self._leave()
class ReadWriteLock(object):
""" This reader writer lock gurantees write order, but not
read order and is generally biased towards allowing writes
if they are available to prevent starvation.
TODO:
* allow user to choose between read/write/random biasing
- currently write biased
- read biased allow N readers in queue
- random is 50/50 choice of next
"""
def __init__(self):
""" Initializes a new instance of the ReadWriteLock
"""
self.queue = [] # the current writer queue
self.lock = threading.Lock() # the underlying condition lock
self.read_condition = threading.Condition(self.lock) # the single reader condition
self.readers = 0 # the number of current readers
self.writer = False # is there a current writer
def __is_pending_writer(self):
return (self.writer # if there is a current writer
or (self.queue # or if there is a waiting writer
and (self.queue[0] != self.read_condition))) # or if the queue head is not a reader
def acquire_reader(self):
""" Notifies the lock that a new reader is requesting
the underlying resource.
"""
with self.lock:
if self.__is_pending_writer(): # if there are existing writers waiting
if self.read_condition not in self.queue: # do not pollute the queue with readers
self.queue.append(self.read_condition) # add the readers in line for the queue
while self.__is_pending_writer(): # until the current writer is finished
self.read_condition.wait(1) # wait on our condition
if self.queue and self.read_condition == self.queue[0]: # if the read condition is at the queue head
self.queue.pop(0) # then go ahead and remove it
self.readers += 1 # update the current number of readers
def acquire_writer(self):
""" Notifies the lock that a new writer is requesting
the underlying resource.
"""
with self.lock:
if self.writer or self.readers: # if we need to wait on a writer or readers
condition = threading.Condition(self.lock) # create a condition just for this writer
self.queue.append(condition) # and put it on the waiting queue
while self.writer or self.readers: # until the write lock is free
condition.wait(1) # wait on our condition
self.queue.pop(0) # remove our condition after our condition is met
self.writer = True # stop other writers from operating
def release_reader(self):
""" Notifies the lock that an existing reader is
finished with the underlying resource.
"""
with self.lock:
self.readers = max(0, self.readers - 1) # readers should never go below 0
if not self.readers and self.queue: # if there are no active readers
self.queue[0].notify_all() # then notify any waiting writers
def release_writer(self):
""" Notifies the lock that an existing writer is
finished with the underlying resource.
"""
with self.lock:
self.writer = False # give up current writing handle
if self.queue: # if someone is waiting in the queue
self.queue[0].notify_all() # wake them up first
else: self.read_condition.notify_all() # otherwise wake up all possible readers
@contextmanager
def get_reader_lock(self):
""" Wrap some code with a reader lock using the
python context manager protocol::
with rwlock.get_reader_lock():
do_read_operation()
"""
try:
self.acquire_reader()
yield self
finally: self.release_reader()
@contextmanager
def get_writer_lock(self):
""" Wrap some code with a writer lock using the
python context manager protocol::
with rwlock.get_writer_lock():
do_read_operation()
"""
try:
self.acquire_writer()
yield self
finally: self.release_writer()
class ThreadSafeDataBlock(BaseModbusDataBlock):
""" This is a simple decorator for a data block. This allows
a user to inject an existing data block which can then be
safely operated on from multiple cocurrent threads.
It should be noted that the choice was made to lock around the
datablock instead of the manager as there is less source of
contention (writes can occur to slave 0x01 while reads can
occur to slave 0x02).
"""
def __init__(self, block):
""" Initialize a new thread safe decorator
:param block: The block to decorate
"""
self.rwlock = ReadWriteLock()
self.block = block
def validate(self, address, count=1):
""" Checks to see if the request is in range
:param address: The starting address
:param count: The number of values to test for
:returns: True if the request in within range, False otherwise
"""
with self.rwlock.get_reader_lock():
return self.block.validate(address, count)
def getValues(self, address, count=1):
""" Returns the requested values of the datastore
:param address: The starting address
:param count: The number of values to retrieve
:returns: The requested values from a:a+c
"""
with self.rwlock.get_reader_lock():
return self.block.getValues(address, count)
def setValues(self, address, values):
""" Sets the requested values of the datastore
:param address: The starting address
:param values: The new values to be set
"""
with self.rwlock.get_writer_lock():
return self.block.setValues(address, values)
if __name__ == "__main__":
class AtomicCounter(object):
def __init__(self, **kwargs):
self.counter = kwargs.get('start', 0)
self.finish = kwargs.get('finish', 1000)
self.lock = threading.Lock()
def increment(self, count=1):
with self.lock:
self.counter += count
def is_running(self):
return self.counter <= self.finish
locker = ReadWriteLock()
readers, writers = AtomicCounter(), AtomicCounter()
def read():
while writers.is_running() and readers.is_running():
with locker.get_reader_lock():
readers.increment()
def write():
while writers.is_running() and readers.is_running():
with locker.get_writer_lock():
writers.increment()
rthreads = [threading.Thread(target=read) for i in range(50)]
wthreads = [threading.Thread(target=write) for i in range(2)]
for t in rthreads + wthreads: t.start()
for t in rthreads + wthreads: t.join()
print("readers[%d] writers[%d]" % (readers.counter, writers.counter))
pymodbus-2.1.0/examples/contrib/tx_messages 0000664 0000000 0000000 00000013602 13355134677 0021117 0 ustar 00root root 0000000 0000000 # ------------------------------------------------------------
# What follows is a collection of encoded messages that can
# be used to test the message-parser. Simply uncomment the
# messages you want decoded and run the message parser with
# the given arguments. What follows is the listing of messages
# that are encoded in each format:
#
# - ReadHoldingRegistersRequest
# - ReadDiscreteInputsRequest
# - ReadInputRegistersRequest
# - ReadCoilsRequest
# - WriteMultipleCoilsRequest
# - WriteMultipleRegistersRequest
# - WriteSingleRegisterRequest
# - WriteSingleCoilRequest
# - ReadWriteMultipleRegistersRequest
# - ReadExceptionStatusRequest
# - GetCommEventCounterRequest
# - GetCommEventLogRequest
# - ReportSlaveIdRequest
# - ReadFileRecordRequest
# - WriteFileRecordRequest
# - MaskWriteRegisterRequest
# - ReadFifoQueueRequest
# - ReadDeviceInformationRequest
# - ReturnQueryDataRequest
# - RestartCommunicationsOptionRequest
# - ReturnDiagnosticRegisterRequest
# - ChangeAsciiInputDelimiterRequest
# - ForceListenOnlyModeRequest
# - ClearCountersRequest
# - ReturnBusMessageCountRequest
# - ReturnBusCommunicationErrorCountRequest
# - ReturnBusExceptionErrorCountRequest
# - ReturnSlaveMessageCountRequest
# - ReturnSlaveNoReponseCountRequest
# - ReturnSlaveNAKCountRequest
# - ReturnSlaveBusyCountRequest
# - ReturnSlaveBusCharacterOverrunCountRequest
# - ReturnIopOverrunCountRequest
# - ClearOverrunCountRequest
# - GetClearModbusPlusRequest
# ------------------------------------------------------------
# Modbus TCP Messages
# ------------------------------------------------------------
# [ MBAP Header ] [ Function Code] [ Data ]
# [ tid ][ pid ][ length ][ uid ]
# 2b 2b 2b 1b 1b Nb
#
# ./message-parser -b -p tcp -f messages
# ------------------------------------------------------------
#000100000006010300120008
#000100000006010200120008
#000100000006010400120008
#000100000006010100120008
#000100000008010f0012000801ff
#0001000000170110001200081000010001000100010001000100010001
#000100000006010600120001
#00010000000601050012ff00
#00010000001b011700120008000000081000010001000100010001000100010001
#0001000000020107
#000100000002010b
#000100000002010c
#0001000000020111
#000100000003011400
#000100000003011500
#00010000000801160012ffff0000
#00010000000401180012
#000100000005012b0e0100
#000100000006010800000000
#000100000006010800010000
#000100000006010800020000
#000100000006010800030000
#000100000006010800040000
#0001000000060108000a0000
#0001000000060108000b0000
#0001000000060108000c0000
#0001000000060108000d0000
#0001000000060108000e0000
#0001000000060108000f0000
#000100000006010800100000
#000100000006010800110000
#000100000006010800120000
#000100000006010800130000
#000100000006010800140000
#000100000006010800150000
# ------------------------------------------------------------
# Modbus RTU Messages
# ------------------------------------------------------------
# [Address ][ Function Code] [ Data ][ CRC ]
# 1b 1b Nb 2b
#
# ./message-parser -b -p rtu -f messages
# ------------------------------------------------------------
#010300120008e409
#010200120008d9c9
#01040012000851c9
#0101001200089dc9
#010f0012000801ff06d6
#0110001200081000010001000100010001000100010001d551
#010600120001e80f
#01050012ff002c3f
#011700120008000000081000010001000100010001000100010001e6f8
#010741e2
#010b41e7
#010c0025
#0111c02c
#0114002f00
#0115002e90
#01160012ffff00004e21
#0118001201d2
#012b0e01007077
#010800000000e00b
#010800010000b1cb
#01080002000041cb
#010800030000100b
#010800040000a1ca
#0108000a0000c009
#0108000b000091c9
#0108000c00002008
#0108000d000071c8
#0108000e000081c8
#0108000f0000d008
#010800100000e1ce
#010800110000b00e
#010800120000400e
#01080013000011ce
#010800140000a00f
#010800150000f1cf
# ------------------------------------------------------------
# Modbus ASCII Messages
# ------------------------------------------------------------
# [ Start ][Address ][ Function ][ Data ][ LRC ][ End ]
# 1c 2c 2c Nc 2c 2c
#
# ./message-parser -a -p ascii -f messages
# ------------------------------------------------------------
#:010300120008E2
#:010200120008E3
#:010400120008E1
#:010100120008E4
#:010F0012000801FFD6
#:0110001200081000010001000100010001000100010001BD
#:010600120001E6
#:01050012FF00E9
#:011700120008000000081000010001000100010001000100010001AE
#:0107F8
#:010BF4
#:010CF3
#:0111EE
#:011400EB
#:011500EA
#:01160012FFFF0000D9
#:01180012D5
#:012B0E0100C5
#:010800000000F7
#:010800010000F6
#:010800020000F5
#:010800030000F4
#:010800040000F3
#:0108000A0000ED
#:0108000B0000EC
#:0108000C0000EB
#:0108000D0000EA
#:0108000E0000E9
#:0108000F0000E8
#:010800100000E7
#:010800110000E6
#:010800120000E5
#:010800130000E4
#:010800140000E3
#:010800150000E2
# ------------------------------------------------------------
# Modbus Binary Messages
# ------------------------------------------------------------
# [ Start ][Address ][ Function ][ Data ][ CRC ][ End ]
# 1b 1b 1b Nb 2b 1b
#
# ./message-parser -b -p binary -f messages
# ------------------------------------------------------------
#7b010300120008e4097d
#7b010200120008d9c97d
#7b01040012000851c97d
#7b0101001200089dc97d
#7b010f0012000801ff06d67d
#7b0110001200081000010001000100010001000100010001d5517d
#7b010600120001e80f7d
#7b01050012ff002c3f7d
#7b011700120008000000081000010001000100010001000100010001e6f87d
#7b010741e27d
#7b010b41e77d
#7b010c00257d
#7b0111c02c7d
#7b0114002f007d
#7b0115002e907d
#7b01160012ffff00004e217d
#7b0118001201d27d
#7b012b0e010070777d
#7b010800000000e00b7d
#7b010800010000b1cb7d
#7b01080002000041cb7d
#7b010800030000100b7d
#7b010800040000a1ca7d
#7b0108000a0000c0097d
#7b0108000b000091c97d
#7b0108000c000020087d
#7b0108000d000071c87d
#7b0108000e000081c87d
#7b0108000f0000d0087d
#7b010800100000e1ce7d
#7b010800110000b00e7d
#7b010800120000400e7d
#7b01080013000011ce7d
#7b010800140000a00f7d
#7b010800150000f1cf7d
pymodbus-2.1.0/examples/functional/ 0000775 0000000 0000000 00000000000 13355134677 0017352 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/examples/functional/README.rst 0000664 0000000 0000000 00000001776 13355134677 0021054 0 ustar 00root root 0000000 0000000 ============================================================================
Pymodbus Functional Tests
============================================================================
Modbus Clients
---------------------------------------------------------------------------
The following can be run to validate the pymodbus clients against a running
modbus instance. For these tests, the following are used as references::
* jamod
* modpoll
Modbus Servers
---------------------------------------------------------------------------
The following can be used to create a null modem loopback for testing the
serial implementations::
* tty0tty (linux)
* com0com (windows)
Specialized Datastores
---------------------------------------------------------------------------
The following can be run to validate the pymodbus specializes datastores.
For these tests, the following are used as references:
* sqlite (for the database datastore)
* redis (for the redis datastore)
* modpoll (for the remote slave datastore)
pymodbus-2.1.0/examples/functional/asynchronous_ascii_client.py 0000664 0000000 0000000 00000001430 13355134677 0025163 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.client.async import ModbusSerialClient as ModbusClient
from base_runner import Runner
class AsynchronousAsciiClient(Runner, unittest.TestCase):
"""
These are the integration tests for the asynchronous
serial ascii client.
"""
def setUp(self):
""" Initializes the test environment """
super(Runner, self).setUp()
self.client = ModbusClient(method='ascii')
def tearDown(self):
""" Cleans up the test environment """
self.client.close()
self.shutdown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/asynchronous_rtu_client.py 0000664 0000000 0000000 00000001441 13355134677 0024707 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.client.async import ModbusSerialClient as ModbusClient
from base_runner import Runner
class AsynchronousRtuClient(Runner, unittest.TestCase):
"""
These are the integration tests for the asynchronous
serial rtu client.
"""
def setUp(self):
""" Initializes the test environment """
super(Runner, self).setUp()
self.client = ModbusClient(method='rtu')
def tearDown(self):
""" Cleans up the test environment """
self.client.close()
super(Runner, self).tearDown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/asynchronous_tcp_client.py 0000664 0000000 0000000 00000002222 13355134677 0024661 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from twisted.internet import reactor, protocol
from pymodbus.constants import Defaults
from pymodbus.client.async import ModbusClientProtocol
from base_runner import Runner
class AsynchronousTcpClient(Runner, unittest.TestCase):
"""
These are the integration tests for the asynchronous
tcp client.
"""
def setUp(self):
""" Initializes the test environment """
def _callback(client): self.client = client
self.initialize(["../tools/reference/diagslave", "-m", "tcp", "-p", "12345"])
defer = protocol.ClientCreator(reactor, ModbusClientProtocol
).connectTCP("localhost", Defaults.Port)
defer.addCallback(_callback)
reactor.run()
def tearDown(self):
""" Cleans up the test environment """
reactor.callLater(1, client.transport.loseConnection)
reactor.callLater(2, reactor.stop)
reactor.shutdown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/asynchronous_udp_client.py 0000664 0000000 0000000 00000001412 13355134677 0024663 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.client.sync import ModbusUdpClient as ModbusClient
from base_runner import Runner
class AsynchronousUdpClient(Runner, unittest.TestCase):
"""
These are the integration tests for the asynchronous
udp client.
"""
def setUp(self):
""" Initializes the test environment """
super(Runner, self).setUp()
self.client = ModbusClient()
def tearDown(self):
""" Cleans up the test environment """
self.client.close()
super(Runner, self).tearDown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/base_context.py 0000664 0000000 0000000 00000004144 13355134677 0022405 0 ustar 00root root 0000000 0000000 import os
import time
from subprocess import Popen as execute
from twisted.internet.defer import Deferred
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
log = logging.getLogger(__name__)
class ContextRunner(object):
"""
This is the base runner class for all the integration tests
"""
__bit_functions = [2,1] # redundant are removed for now
__reg_functions = [4,3] # redundant are removed for now
def initialize(self, service=None):
""" Initializes the test environment """
if service:
self.fnull = open(os.devnull, 'w')
self.service = execute(service, stdout=self.fnull, stderr=self.fnull)
log.debug("%s service started: %s", service, self.service.pid)
time.sleep(0.2)
else: self.service = None
log.debug("%s context started", self.context)
def shutdown(self):
""" Cleans up the test environment """
try:
if self.service:
self.service.kill()
self.fnull.close()
self.context.reset()
except: pass
log.debug("%s context stopped" % self.context)
def testDataContextRegisters(self):
""" Test that the context gets and sets registers """
address = 10
values = [0x1234] * 32
for fx in self.__reg_functions:
self.context.setValues(fx, address, values)
result = self.context.getValues(fx, address, len(values))
self.assertEquals(len(result), len(values))
self.assertEquals(result, values)
def testDataContextDiscretes(self):
""" Test that the context gets and sets discretes """
address = 10
values = [True] * 32
for fx in self.__bit_functions:
self.context.setValues(fx, address, values)
result = self.context.getValues(fx, address, len(values))
self.assertEquals(len(result), len(values))
self.assertEquals(result, values)
pymodbus-2.1.0/examples/functional/base_runner.py 0000664 0000000 0000000 00000005740 13355134677 0022235 0 ustar 00root root 0000000 0000000 import os
import time
from subprocess import Popen as execute
from twisted.internet.defer import Deferred
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
log = logging.getLogger(__name__)
class Runner(object):
"""
This is the base runner class for all the integration tests
"""
def initialize(self, service):
""" Initializes the test environment """
self.fnull = open(os.devnull, 'w')
self.server = execute(service, stdout=self.fnull, stderr=self.fnull)
log.debug("%s service started: %s", service, self.server.pid)
time.sleep(0.2)
def shutdown(self):
""" Cleans up the test environment """
self.server.kill()
self.fnull.close()
log.debug("service stopped")
def testReadWriteCoil(self):
rq = self.client.write_coil(1, True)
rr = self.client.read_coils(1,1)
self._validate(rq, lambda r: not r.isError())
self._validate(rr, lambda r: r.bits[0] == True)
def testReadWriteCoils(self):
rq = self.client.write_coils(1, [True]*8)
rr = self.client.read_coils(1,8)
self._validate(rq, lambda r: not r.isError())
self._validate(rr, lambda r: r.bits == [True]*8)
def testReadWriteDiscreteRegisters(self):
rq = self.client.write_coils(1, [False]*8)
rr = self.client.read_discrete_inputs(1,8)
self._validate(rq, lambda r: not r.isError())
self._validate(rr, lambda r: r.bits == [False]*8)
def testReadWriteHoldingRegisters(self):
rq = self.client.write_register(1, 10)
rr = self.client.read_holding_registers(1,1)
self._validate(rq, lambda r: not r.isError())
self._validate(rr, lambda r: r.registers[0] == 10)
def testReadWriteInputRegisters(self):
rq = self.client.write_registers(1, [10]*8)
rr = self.client.read_input_registers(1,8)
self._validate(rq, lambda r: not r.isError())
self._validate(rr, lambda r: r.registers == [10]*8)
def testReadWriteRegistersTogether(self):
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
rq = self.client.readwrite_registers(**arguments)
rr = self.client.read_input_registers(1,8)
self._validate(rq, lambda r: not r.isError())
self._validate(rr, lambda r: r.registers == [20]*8)
def _validate(self, result, test):
""" Validate the result whether it is a result or a deferred.
:param result: The result to _validate
:param callback: The test to _validate
"""
if isinstance(result, Deferred):
deferred.callback(lambda : self.assertTrue(test(result)))
deferred.errback(lambda _: self.assertTrue(False))
else: self.assertTrue(test(result))
pymodbus-2.1.0/examples/functional/database_slave_context.py 0000664 0000000 0000000 00000001710 13355134677 0024425 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest, os
from pymodbus.datastore.database import DatabaseSlaveContext
from base_context import ContextRunner
class DatabaseSlaveContextTest(ContextRunner, unittest.TestCase):
"""
These are the integration tests for using the redis
slave context.
"""
__database = 'sqlite:///pymodbus-test.db'
def setUp(self):
""" Initializes the test environment """
path = './' + self.__database.split('///')[1]
if os.path.exists(path): os.remove(path)
self.context = DatabaseSlaveContext(database=self.__database)
self.initialize()
def tearDown(self):
""" Cleans up the test environment """
self.context._connection.close()
self.shutdown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/memory_slave_context.py 0000775 0000000 0000000 00000002025 13355134677 0024174 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.datastore.context import ModbusSlaveContext
from pymodbus.datastore.store import ModbusSequentialDataBlock
from base_context import ContextRunner
class MemorySlaveContextTest(ContextRunner, unittest.TestCase):
"""
These are the integration tests for using the in memory
slave context.
"""
def setUp(self):
""" Initializes the test environment """
self.context = ModbusSlaveContext(**{
'di' : ModbusSequentialDataBlock(0, [0]*100),
'co' : ModbusSequentialDataBlock(0, [0]*100),
'ir' : ModbusSequentialDataBlock(0, [0]*100),
'hr' : ModbusSequentialDataBlock(0, [0]*100)})
self.initialize()
def tearDown(self):
""" Cleans up the test environment """
self.shutdown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/redis_slave_context.py 0000664 0000000 0000000 00000001614 13355134677 0023772 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
import os
from subprocess import Popen as execute
from pymodbus.datastore.modredis import RedisSlaveContext
from base_context import ContextRunner
class RedisSlaveContextTest(ContextRunner, unittest.TestCase):
"""
These are the integration tests for using the redis
slave context.
"""
def setUp(self):
""" Initializes the test environment """
self.context = RedisSlaveContext() # the redis client will block, so no wait needed
self.initialize("redis-server")
def tearDown(self):
""" Cleans up the test environment """
self.server.kill()
self.fnull.close()
self.shutdown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/remote_slave_context.py 0000664 0000000 0000000 00000002001 13355134677 0024146 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.datastore.remote import RemoteSlaveContext
from base_context import ContextRunner
class RemoteSlaveContextTest(ContextRunner, unittest.TestCase):
"""
These are the integration tests for using the redis
slave context.
"""
def setUp(self):
""" Initializes the test environment """
self.context = RemoteSlaveContext(client=None) # for the log statment
self.initialize(["../tools/reference/diagslave", "-m", "tcp", "-p", "12345"])
self.client = ModbusTcpClient(port=12345)
self.context = RemoteSlaveContext(client=self.client)
def tearDown(self):
""" Cleans up the test environment """
self.client.close()
self.shutdown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/synchronous_ascii_client.py 0000664 0000000 0000000 00000001750 13355134677 0025027 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from base_runner import Runner
class SynchronousAsciiClient(Runner, unittest.TestCase):
"""
These are the integration tests for the synchronous
serial ascii client.
"""
def setUp(self):
""" Initializes the test environment """
super(Runner, self).setUp()
# "../tools/nullmodem/linux/run",
self.initialize(["../tools/reference/diagslave", "-m", "ascii", "/dev/pts/14"])
self.client = ModbusClient(method='ascii', timeout=0.2, port='/dev/pts/13')
self.client.connect()
def tearDown(self):
""" Cleans up the test environment """
self.client.close()
super(Runner, self).tearDown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/synchronous_rtu_client.py 0000664 0000000 0000000 00000001663 13355134677 0024554 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from base_runner import Runner
class SynchronousRtuClient(Runner, unittest.TestCase):
"""
These are the integration tests for the synchronous
serial rtu client.
"""
def setUp(self):
""" Initializes the test environment """
super(Runner, self).setUp()
self.initialize(["../tools/reference/diagslave", "-m", "rtu", "/dev/pts/14"])
self.client = ModbusClient(method='rtu', timeout=0.2, port='/dev/pts/13')
self.client.connect()
def tearDown(self):
""" Cleans up the test environment """
self.client.close()
super(Runner, self).tearDown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/synchronous_tcp_client.py 0000664 0000000 0000000 00000001465 13355134677 0024530 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from base_runner import Runner
class SynchronousTcpClient(Runner, unittest.TestCase):
"""
These are the integration tests for the synchronous
tcp client.
"""
def setUp(self):
""" Initializes the test environment """
self.initialize(["../tools/reference/diagslave", "-m", "tcp", "-p", "12345"])
self.client = ModbusClient(port=12345)
def tearDown(self):
""" Cleans up the test environment """
self.client.close()
self.shutdown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/functional/synchronous_udp_client.py 0000664 0000000 0000000 00000001410 13355134677 0024520 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import unittest
from pymodbus.client.sync import ModbusUdpClient as ModbusClient
from base_runner import Runner
class SynchronousUdpClient(Runner, unittest.TestCase):
"""
These are the integration tests for the synchronous
udp client.
"""
def setUp(self):
""" Initializes the test environment """
super(Runner, self).setUp()
self.client = ModbusClient()
def tearDown(self):
""" Cleans up the test environment """
self.client.close()
super(Runner, self).tearDown()
# --------------------------------------------------------------------------- #
# Main
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
unittest.main()
pymodbus-2.1.0/examples/gui/ 0000775 0000000 0000000 00000000000 13355134677 0015774 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/examples/gui/bottle/ 0000775 0000000 0000000 00000000000 13355134677 0017265 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/examples/gui/bottle/frontend.py 0000664 0000000 0000000 00000025513 13355134677 0021464 0 ustar 00root root 0000000 0000000 """
Pymodbus Web Frontend
=======================================
This is a simple web frontend using bottle as the web framework.
This can be hosted using any wsgi adapter.
"""
from __future__ import print_function
import json, inspect
from bottle import route, request, Bottle
from bottle import static_file
from bottle import jinja2_template as template
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# REST API
# --------------------------------------------------------------------------- #
class Response(object):
"""
A collection of common responses for the frontend api
"""
success = { 'status' : 200 }
failure = { 'status' : 500 }
class ModbusApiWebApp(object):
"""
This is the web REST api interace into the pymodbus
service. It can be consumed by any utility that can
make web requests (javascript).
"""
_namespace = '/api/v1'
def __init__(self, server):
""" Initialize a new instance of the ModbusApi
:param server: The current server instance
"""
self._server = server
#---------------------------------------------------------------------#
# Device API
#---------------------------------------------------------------------#
def get_device(self):
return {
'mode' : self._server.control.Mode,
'delimiter' : self._server.control.Delimiter,
'readonly' : self._server.control.ListenOnly,
'identity' : self._server.control.Identity.summary(),
'counters' : dict(self._server.control.Counter),
'diagnostic' : self._server.control.getDiagnosticRegister(),
}
def get_device_identity(self):
return {
'identity' : dict(self._server.control.Identity)
}
def get_device_counters(self):
return {
'counters' : dict(self._server.control.Counter)
}
def get_device_events(self):
return {
'events' : self._server.control.Events
}
def get_device_plus(self):
return {
'plus' : dict(self._server.control.Plus)
}
def delete_device_events(self):
self._server.control.clearEvents()
return Response.success
def get_device_host(self):
return {
'hosts' : list(self._server.access)
}
def post_device_host(self):
value = request.forms.get('host')
if value:
self._server.access.add(value)
return Response.success
def delete_device_host(self):
value = request.forms.get('host')
if value:
self._server.access.remove(value)
return Response.success
def post_device_delimiter(self):
value = request.forms.get('delimiter')
if value:
self._server.control.Delimiter = value
return Response.success
def post_device_mode(self):
value = request.forms.get('mode')
if value:
self._server.control.Mode = value
return Response.success
def post_device_reset(self):
self._server.control.reset()
return Response.success
#---------------------------------------------------------------------#
# Datastore Get API
#---------------------------------------------------------------------#
def __get_data(self, store, address, count, slave='00'):
try:
address, count = int(address), int(count)
context = self._server.store[int(store)]
values = context.getValues(store, address, count)
values = dict(zip(range(address, address + count), values))
result = { 'data' : values }
result.update(Response.success)
return result
except Exception as ex:
log.error(ex)
return Response.failure
def get_coils(self, address='0', count='1'):
return self.__get_data(1, address, count)
def get_discretes(self, address='0', count='1'):
return self.__get_data(2, address, count)
def get_holdings(self, address='0', count='1'):
return self.__get_data(3, address, count)
def get_inputs(self, address='0', count='1'):
return self.__get_data(4, address, count)
#---------------------------------------------------------------------#
# Datastore Update API
#---------------------------------------------------------------------#
def __set_data(self, store, address, values, slave='00'):
try:
address = int(address)
values = json.loads(values)
print(values)
context = self._server.store[int(store)]
context.setValues(store, address, values)
return Response.success
except Exception as ex:
log.error(ex)
return Response.failure
def post_coils(self, address='0'):
values = request.forms.get('data')
return self.__set_data(1, address, values)
def post_discretes(self, address='0'):
values = request.forms.get('data')
return self.__set_data(2, address, values)
def post_holding(self, address='0'):
values = request.forms.get('data')
return self.__set_data(3, address, values)
def post_inputs(self, address='0'):
values = request.forms.get('data')
return self.__set_data(4, address, values)
#---------------------------------------------------------------------#
# webpage routes
#---------------------------------------------------------------------#
def register_web_routes(application, register):
""" A helper method to register the default web routes of
a single page application.
:param application: The application instance to register
:param register: The bottle instance to register the application with
"""
def get_index_file():
return template('index.html')
def get_static_file(filename):
return static_file(filename, root='./media')
register.route('/', method='GET', name='get_index_file')(get_index_file)
register.route('/media/', method='GET', name='get_static_file')(get_static_file)
# --------------------------------------------------------------------------- #
# Configurations
# --------------------------------------------------------------------------- #
def register_api_routes(application, register):
""" A helper method to register the routes of an application
based on convention. This is easier to manage than having to
decorate each method with a static route name.
:param application: The application instance to register
:param register: The bottle instance to register the application with
"""
log.info("installing application routes:")
methods = inspect.getmembers(application)
methods = filter(lambda n: not n[0].startswith('_'), methods)
for method, func in dict(methods).iteritems():
pieces = method.split('_')
verb, path = pieces[0], pieces[1:]
args = inspect.getargspec(func).args[1:]
args = ['<%s>' % arg for arg in args]
args = '/'.join(args)
args = '' if len(args) == 0 else '/' + args
path.insert(0, application._namespace)
path = '/'.join(path) + args
log.info("%6s: %s" % (verb, path))
register.route(path, method=verb, name=method)(func)
def build_application(server):
""" Helper method to create and initiailze a bottle application
:param server: The modbus server to pull instance data from
:returns: An initialied bottle application
"""
log.info("building web application")
api = ModbusApiWebApp(server)
register = Bottle()
register_api_routes(api, register)
register_web_routes(api, register)
return register
# --------------------------------------------------------------------------- #
# Start Methods
# --------------------------------------------------------------------------- #
def RunModbusFrontend(server, port=8080):
""" Helper method to host bottle in twisted
:param server: The modbus server to pull instance data from
:param port: The port to host the service on
"""
from bottle import TwistedServer, run
application = build_application(server)
run(app=application, server=TwistedServer, port=port)
def RunDebugModbusFrontend(server, port=8080):
""" Helper method to start the bottle server
:param server: The modbus server to pull instance data from
:param port: The port to host the service on
"""
from bottle import run
application = build_application(server)
run(app=application, port=port)
if __name__ == '__main__':
# ------------------------------------------------------------
# an example server configuration
# ------------------------------------------------------------
from pymodbus.server.async import ModbusServerFactory
from pymodbus.constants import Defaults
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from twisted.internet import reactor
# ------------------------------------------------------------
# initialize the identity
# ------------------------------------------------------------
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '1.0'
# ------------------------------------------------------------
# initialize the datastore
# ------------------------------------------------------------
store = ModbusSlaveContext(
di = ModbusSequentialDataBlock(0, [17]*100),
co = ModbusSequentialDataBlock(0, [17]*100),
hr = ModbusSequentialDataBlock(0, [17]*100),
ir = ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)
# ------------------------------------------------------------
# initialize the factory
# ------------------------------------------------------------
address = ("", Defaults.Port)
factory = ModbusServerFactory(context, None, identity)
# ------------------------------------------------------------
# start the servers
# ------------------------------------------------------------
log.info("Starting Modbus TCP Server on %s:%s" % address)
reactor.listenTCP(address[1], factory, interface=address[0])
RunDebugModbusFrontend(factory)
pymodbus-2.1.0/examples/gui/bottle/requirements.txt 0000664 0000000 0000000 00000000316 13355134677 0022551 0 ustar 00root root 0000000 0000000 # -------------------------------------------------------------------
# if you want to use this frontend uncomment these
# -------------------------------------------------------------------
bottle==0.11.2
pymodbus-2.1.0/examples/gui/bottle/views/ 0000775 0000000 0000000 00000000000 13355134677 0020422 5 ustar 00root root 0000000 0000000 pymodbus-2.1.0/examples/gui/bottle/views/index.html 0000664 0000000 0000000 00000010436 13355134677 0022423 0 ustar 00root root 0000000 0000000
{% block title %}pymodbus | server{% endblock %}