Skip to content

Commit

Permalink
1. #284 Async servers - Option to start reactor outside Start<server>…
Browse files Browse the repository at this point in the history
…Server function

2. #283 Fix BinaryPayloadDecoder/Builder issues when using with pymodbus server
3. #278 Fix issue with sync/async servers failing to handle requests with transaction id > 255
4. #221 Move timing and transcational logic to framers for sync clients
5. #221 More debug logs for sync clients
6. Misc updates with examples and minor enhancements
  • Loading branch information
dhoomakethu committed Apr 9, 2018
1 parent 689708a commit 900db55
Show file tree
Hide file tree
Showing 32 changed files with 1,694 additions and 1,107 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Version 1.5.0

* 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 `Start<Tcp/Serial/Udo>Server(...,defer_reactor_run=True)`
* Fix Misc examples

Version 1.4.0
Expand Down
47 changes: 47 additions & 0 deletions doc/source/library/pymodbus.framer.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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:

2 changes: 2 additions & 0 deletions doc/source/library/pymodbus.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ Subpackages

pymodbus.client
pymodbus.datastore
pymodbus.framer
pymodbus.internal
pymodbus.server


Submodules
----------

Expand Down
43 changes: 34 additions & 9 deletions examples/common/asynchronous_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
# choose the requested modbus protocol
# --------------------------------------------------------------------------- #
from pymodbus.client.async import ModbusClientProtocol
#from pymodbus.client.async import ModbusUdpClientProtocol
from pymodbus.client.async import ModbusUdpClientProtocol
from pymodbus.framer.rtu_framer import ModbusRtuFramer

# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

Expand All @@ -34,7 +37,6 @@
def dassert(deferred, callback):
def _assertor(value):
assert value

deferred.addCallback(lambda r: _assertor(callback(r)))
deferred.addErrback(lambda _: _assertor(False))

Expand All @@ -47,8 +49,20 @@ def _assertor(value):
# --------------------------------------------------------------------------- #


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
Expand All @@ -61,7 +75,16 @@ def exampleRequests(client):
# deferred assert helper(dassert).
# --------------------------------------------------------------------------- #

UNIT = 0x01

UNIT = 0x00


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)
Expand Down Expand Up @@ -99,12 +122,8 @@ def beginAsynchronousTest(client):
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
Expand Down Expand Up @@ -134,5 +153,11 @@ def beginAsynchronousTest(client):
if __name__ == "__main__":
defer = protocol.ClientCreator(
reactor, ModbusClientProtocol).connectTCP("localhost", 5020)

# TCP server with a different framer

# defer = protocol.ClientCreator(
# reactor, ModbusClientProtocol, framer=ModbusRtuFramer).connectTCP(
# "localhost", 5020)
defer.addCallback(beginAsynchronousTest)
reactor.run()
10 changes: 6 additions & 4 deletions examples/common/asynchronous_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger("pymodbus")
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/ttyp0"
SERIAL_PORT = "/dev/ptyp0"
STATUS_REGS = (1, 2)
STATUS_COILS = (1, 3)
CLIENT_DELAY = 1
Expand Down Expand Up @@ -173,7 +175,7 @@ def write(self, response):

def main():
log.debug("Initializing the client")
framer = ModbusFramer(ClientDecoder())
framer = ModbusFramer(ClientDecoder(), client=None)
reader = LoggingLineReader()
factory = ExampleFactory(framer, reader)
SerialModbusClient(factory, SERIAL_PORT, reactor)
Expand Down
45 changes: 36 additions & 9 deletions examples/common/asynchronous_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
from pymodbus.transaction import (ModbusRtuFramer,
ModbusAsciiFramer,
ModbusBinaryFramer)

# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

Expand Down Expand Up @@ -101,18 +105,41 @@ def run_async_server():
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '1.0'
identity.MajorMinorRevision = '1.5'

# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #


# TCP Server

StartTcpServer(context, identity=identity, address=("localhost", 5020))
# StartUdpServer(context, identity=identity, address=("localhost", 502))
# StartSerialServer(context, identity=identity,
# port='/dev/pts/3', framer=ModbusRtuFramer)
# StartSerialServer(context, identity=identity,
# port='/dev/pts/3', framer=ModbusAsciiFramer)

# 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__":
Expand Down
78 changes: 53 additions & 25 deletions examples/common/modbus_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@
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
logging.basicConfig()
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.INFO)
log.setLevel(logging.DEBUG)


def run_binary_payload_ex():
# ----------------------------------------------------------------------- #
# We are going to use a simple client to send our requests
# ----------------------------------------------------------------------- #
client = ModbusClient('127.0.0.1', port=5440)
client = ModbusClient('127.0.0.1', port=5020)
client.connect()

# ----------------------------------------------------------------------- #
Expand Down Expand Up @@ -67,19 +71,36 @@ def run_binary_payload_ex():
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #

# ----------------------------------------------------------------------- #
builder = BinaryPayloadBuilder(byteorder=Endian.Little,
wordorder=Endian.Big)
builder = BinaryPayloadBuilder(byteorder=Endian.Big,
wordorder=Endian.Little)
builder.add_string('abcdefgh')
builder.add_32bit_float(22.34)
builder.add_16bit_uint(0x1234)
builder.add_16bit_uint(0x5678)
builder.add_8bit_int(0x12)
builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0])
builder.add_32bit_uint(0x12345678)
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_64bit_int(0x1234567890ABCDEF)
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
Expand All @@ -95,7 +116,7 @@ def run_binary_payload_ex():
# - an 8 bit int 0x12
# - an 8 bit bitstring [0,1,0,1,1,0,1,0]
# ----------------------------------------------------------------------- #
address = 0x00
address = 0x0
count = len(payload)
result = client.read_holding_registers(address, count, unit=1)
print("-" * 60)
Expand All @@ -105,19 +126,26 @@ def run_binary_payload_ex():
print("\n")
decoder = BinaryPayloadDecoder.fromRegisters(result.registers,
byteorder=Endian.Little,
wordorder=Endian.Big)
decoded = {
'string': decoder.decode_string(8),
'float': decoder.decode_32bit_float(),
'16uint': decoder.decode_16bit_uint(),
'ignored': decoder.skip_bytes(2),
'8int': decoder.decode_8bit_int(),
'bits': decoder.decode_bits(),
"32uints": decoder.decode_32bit_uint(),
"32ints": decoder.decode_32bit_int(),
"64ints": decoder.decode_64bit_int(),
}

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)
Expand Down
Loading

0 comments on commit 900db55

Please sign in to comment.