Skip to content

Commit 7a0ad56

Browse files
authored
Merge pull request #327 from riptideio/#326-Use-Existing-Loop
#326 Handle asyncio loop
2 parents f5c226b + 7fc559b commit 7a0ad56

File tree

6 files changed

+119
-5
lines changed

6 files changed

+119
-5
lines changed

README.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ permissions or a virtualenv currently running)::
104104
easy_install -U pymodbus
105105
pip install -U pymodbus
106106

107+
To Install pymodbus with twisted support run
108+
.. code-block:: python
109+
pip install -U pymodbus[twisted]
110+
111+
To Install pymodbus with tornado support run
112+
113+
.. code-block:: python
114+
pip install -U pymodbus[tornado]
115+
107116
Otherwise you can pull the trunk source and install from there::
108117

109118
git clone git://github.com/bashwork/pymodbus.git

examples/common/async_asyncio_client.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
sys.stderr("This example needs to be run only on python 3.4 and above")
2626
sys.exit(1)
2727

28+
from threading import Thread
29+
import time
2830
# --------------------------------------------------------------------------- #
2931
# configure the client logging
3032
# --------------------------------------------------------------------------- #
@@ -129,8 +131,81 @@ async def start_async_test(client):
129131
await asyncio.sleep(1)
130132

131133

132-
if __name__ == '__main__':
134+
def run_with_not_running_loop():
135+
"""
136+
A loop is created and is passed to ModbusClient factory to be used.
133137
138+
:return:
139+
"""
140+
log.debug("Running Async client with asyncio loop not yet started")
141+
log.debug("------------------------------------------------------")
142+
loop = asyncio.new_event_loop()
143+
assert not loop.is_running()
144+
new_loop, client = ModbusClient(schedulers.ASYNC_IO, port=5020, loop=loop)
145+
loop.run_until_complete(start_async_test(client.protocol))
146+
loop.close()
147+
log.debug("--------------RUN_WITH_NOT_RUNNING_LOOP---------------")
148+
log.debug("")
149+
150+
151+
def run_with_already_running_loop():
152+
"""
153+
An already running loop is passed to ModbusClient Factory
154+
:return:
155+
"""
156+
log.debug("Running Async client with asyncio loop already started")
157+
log.debug("------------------------------------------------------")
158+
159+
def done(future):
160+
log.info("Done !!!")
161+
162+
def start_loop(loop):
163+
"""
164+
Start Loop
165+
:param loop:
166+
:return:
167+
"""
168+
asyncio.set_event_loop(loop)
169+
loop.run_forever()
170+
171+
loop = asyncio.new_event_loop()
172+
t = Thread(target=start_loop, args=[loop])
173+
t.daemon = True
174+
# Start the loop
175+
t.start()
176+
assert loop.is_running()
177+
asyncio.set_event_loop(loop)
178+
loop, client = ModbusClient(schedulers.ASYNC_IO, port=5020, loop=loop)
179+
future = asyncio.run_coroutine_threadsafe(
180+
start_async_test(client.protocol), loop=loop)
181+
future.add_done_callback(done)
182+
while not future.done():
183+
time.sleep(0.1)
184+
loop.stop()
185+
log.debug("--------DONE RUN_WITH_ALREADY_RUNNING_LOOP-------------")
186+
log.debug("")
187+
188+
189+
def run_with_no_loop():
190+
"""
191+
ModbusClient Factory creates a loop.
192+
:return:
193+
"""
134194
loop, client = ModbusClient(schedulers.ASYNC_IO, port=5020)
135195
loop.run_until_complete(start_async_test(client.protocol))
136196
loop.close()
197+
198+
199+
if __name__ == '__main__':
200+
# Run with No loop
201+
log.debug("Running Async client")
202+
log.debug("------------------------------------------------------")
203+
run_with_no_loop()
204+
205+
# Run with loop not yet started
206+
run_with_not_running_loop()
207+
208+
# Run with already running loop
209+
run_with_already_running_loop()
210+
log.debug("---------------------RUN_WITH_NO_LOOP-----------------")
211+
log.debug("")

pymodbus/client/async/factory/tcp.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,17 @@ def async_io_factory(host="127.0.0.1", port=Defaults.Port, framer=None,
9191
"""
9292
import asyncio
9393
from pymodbus.client.async.asyncio import init_tcp_client
94-
loop = kwargs.get("loop") or asyncio.get_event_loop()
94+
loop = kwargs.get("loop") or asyncio.new_event_loop()
9595
proto_cls = kwargs.get("proto_cls", None)
96-
cor = init_tcp_client(proto_cls, loop, host, port)
97-
client = loop.run_until_complete(asyncio.gather(cor))[0]
96+
if not loop.is_running():
97+
asyncio.set_event_loop(loop)
98+
cor = init_tcp_client(proto_cls, loop, host, port)
99+
client = loop.run_until_complete(asyncio.gather(cor))[0]
100+
else:
101+
cor = init_tcp_client(proto_cls, loop, host, port)
102+
future = asyncio.run_coroutine_threadsafe(cor, loop=loop)
103+
client = future.result()
104+
98105
return loop, client
99106

100107

pymodbus/payload.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,17 @@ def to_registers(self):
124124
_logger.debug(payload)
125125
return payload
126126

127+
def to_coils(self):
128+
"""Convert the payload buffer into a coil
129+
layout that can be used as a context block.
130+
131+
:returns: The coil layout to use as a block
132+
"""
133+
payload = self.to_registers()
134+
coils = [bool(int(bit)) for reg
135+
in payload[1:] for bit in format(reg, '016b')]
136+
return coils
137+
127138
def build(self):
128139
""" Return the payload buffer as a list
129140

setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@
8787
'pyasn1 >= 0.1.4',
8888
'pycrypto >= 2.6'
8989
],
90+
'tornado': [
91+
'tornado >= 4.5.3'
92+
]
9093
},
9194
test_suite='nose.collector',
9295
cmdclass=command_classes,

test/test_payload.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,24 @@ def testPayloadBuilderReset(self):
103103

104104
def testPayloadBuilderWithRawPayload(self):
105105
""" Test basic bit message encoding/decoding """
106-
builder = BinaryPayloadBuilder([b'\x12', b'\x34', b'\x56', b'\x78'], repack=True)
106+
_coils1 = [False, True, True, True, True, False, False, False, False,
107+
True, False, True, False, True, True, False]
108+
_coils2 = [False, True, False, True, False, True, True, False,
109+
False, True, True, True, True, False, False, False]
110+
111+
builder = BinaryPayloadBuilder([b'\x12', b'\x34', b'\x56', b'\x78'],
112+
repack=True)
107113
self.assertEqual(b'\x12\x34\x56\x78', builder.to_string())
108114
self.assertEqual([13330, 30806], builder.to_registers())
109115

116+
self.assertEqual(_coils1, builder.to_coils())
117+
110118
builder = BinaryPayloadBuilder([b'\x12', b'\x34', b'\x56', b'\x78'],
111119
byteorder=Endian.Big)
112120
self.assertEqual(b'\x12\x34\x56\x78', builder.to_string())
113121
self.assertEqual([4660, 22136], builder.to_registers())
114122
self.assertEqual('\x12\x34\x56\x78', str(builder))
123+
self.assertEqual(_coils2, builder.to_coils())
115124

116125
# ----------------------------------------------------------------------- #
117126
# Payload Decoder Tests

0 commit comments

Comments
 (0)