Skip to content

Commit 94a8831

Browse files
authored
Merge pull request #268 from cbergmiller/fix/serial_async_client
Fix AsyncioModbusSerialClient (serial options and create_serial_connection usage)
2 parents eab4dce + 960bc0f commit 94a8831

File tree

3 files changed

+38
-57
lines changed

3 files changed

+38
-57
lines changed

pymodbus/client/async/asyncio/__init__.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,8 @@ class AsyncioModbusSerialClient(object):
659659
transport = None
660660
framer = None
661661

662-
def __init__(self, port, protocol_class=None, framer=None, loop=None):
662+
def __init__(self, port, protocol_class=None, framer=None, loop=None,
663+
baudrate=9600, bytesize=8, parity='N', stopbits=1):
663664
"""
664665
Initializes Asyncio Modbus Serial Client
665666
:param port: Port to connect
@@ -674,30 +675,31 @@ def __init__(self, port, protocol_class=None, framer=None, loop=None):
674675
#: Event loop to use.
675676
self.loop = loop or asyncio.get_event_loop()
676677
self.port = port
678+
self.baudrate = baudrate
679+
self.bytesize = bytesize
680+
self.parity = parity
681+
self.stopbits = stopbits
677682
self.framer = framer
678-
self._connected = False
683+
self._connected_event = asyncio.Event()
679684

680685
def stop(self):
681686
"""
682687
Stops connection
683688
:return:
684689
"""
685-
if self._connected:
690+
if self._connected.is_set():
686691
if self.protocol:
687692
if self.protocol.transport:
688693
self.protocol.transport.close()
689694

690695
def _create_protocol(self):
691-
"""
692-
Factory function to create initialized protocol instance.
693-
"""
694-
from serial_asyncio import create_serial_connection
695-
696-
def factory():
697-
return self.protocol_class(framer=self.framer)
696+
protocol = self.protocol_class(framer=self.framer)
697+
protocol.factory = self
698+
return protocol
698699

699-
cor = create_serial_connection(self.loop, factory, self.port)
700-
return cor
700+
@property
701+
def _connected(self):
702+
return self._connected_event.is_set()
701703

702704
@asyncio.coroutine
703705
def connect(self):
@@ -707,19 +709,24 @@ def connect(self):
707709
"""
708710
_logger.debug('Connecting.')
709711
try:
710-
yield from self.loop.create_connection(self._create_protocol)
711-
_logger.info('Connected to %s:%s.' % (self.host, self.port))
712+
from serial_asyncio import create_serial_connection
713+
714+
yield from create_serial_connection(
715+
self.loop, self._create_protocol, self.port, baudrate=self.baudrate,
716+
bytesize=self.bytesize, stopbits=self.stopbits
717+
)
718+
yield from self._connected_event.wait()
719+
_logger.info('Connected to %s', self.port)
712720
except Exception as ex:
713-
_logger.warning('Failed to connect: %s' % ex)
714-
# asyncio.async(self._reconnect(), loop=self.loop)
721+
_logger.warning('Failed to connect: %s', ex)
715722

716723
def protocol_made_connection(self, protocol):
717724
"""
718725
Protocol notification of successful connection.
719726
"""
720727
_logger.info('Protocol made connection.')
721728
if not self._connected:
722-
self._connected = True
729+
self._connected_event.set()
723730
self.protocol = protocol
724731
else:
725732
_logger.error('Factory protocol connect '
@@ -735,7 +742,7 @@ def protocol_lost_connection(self, protocol):
735742
_logger.error('Factory protocol callback called'
736743
' from unexpected protocol instance.')
737744

738-
self._connected = False
745+
self._connected_event.clear()
739746
self.protocol = None
740747
# if self.host:
741748
# asyncio.async(self._reconnect(), loop=self.loop)

pymodbus/client/async/factory/serial.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def async_io_factory(port=None, framer=None, **kwargs):
8484
Factory to create asyncio based async serial clients
8585
:param port: Serial port
8686
:param framer: Modbus Framer
87-
:param kwargs:
87+
:param kwargs: Serial port options
8888
:return: asyncio event loop and serial client
8989
"""
9090
import asyncio
@@ -101,11 +101,9 @@ def async_io_factory(port=None, framer=None, **kwargs):
101101
import sys
102102
sys.exit(1)
103103

104-
client = AsyncioModbusSerialClient(port, proto_cls, framer, loop)
105-
coro = client._create_protocol()
106-
transport, protocol = loop.run_until_complete(asyncio.gather(coro))[0]
107-
client.transport = transport
108-
client.protocol = protocol
104+
client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)
105+
coro = client.connect()
106+
loop.run_until_complete(coro)
109107
return loop, client
110108

111109

test/test_client_async.py

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,10 @@
3939
# ---------------------------------------------------------------------------#
4040

4141

42-
def mock_create_serial_connection(a, b, port):
43-
ser = MagicMock()
44-
ser.port = port
45-
protocol = b()
46-
transport = SerialTransport(a, protocol, ser)
47-
protocol.transport = transport
48-
return transport, protocol
49-
50-
5142
def mock_asyncio_gather(coro):
5243
return coro
5344

5445

55-
def mock_asyncio_run_untill_complete(val):
56-
transport, protocol = val
57-
protocol._connected = True
58-
return ([transport, protocol], )
59-
60-
6146
class TestAsynchronousClient(object):
6247
"""
6348
This is the unittest for the pymodbus.client.async module
@@ -237,36 +222,27 @@ def testSerialAsyncioClientPython2(self):
237222
assert pytest_wrapped_e.value.code == 1
238223

239224
@pytest.mark.skipif(not IS_PYTHON3 or PYTHON_VERSION < (3, 4), reason="requires python3.4 or above")
240-
@patch("serial_asyncio.create_serial_connection", side_effect=mock_create_serial_connection)
241225
@patch("asyncio.get_event_loop")
242226
@patch("asyncio.gather", side_effect=mock_asyncio_gather)
243227
@pytest.mark.parametrize("method, framer", [("rtu", ModbusRtuFramer),
244228
("socket", ModbusSocketFramer),
245229
("binary", ModbusBinaryFramer),
246230
("ascii", ModbusAsciiFramer)])
247-
def testSerialAsyncioClient(self, mock_gather, mock_event_loop, mock_serial_connection, method, framer):
231+
def testSerialAsyncioClient(self, mock_gather, mock_event_loop, method, framer):
248232
"""
249-
Test Serial async asyncio client exits on python2
233+
Test that AsyncModbusSerialClient instantiates AsyncioModbusSerialClient for asyncio scheduler.
250234
:return:
251235
"""
252236
loop = asyncio.get_event_loop()
253-
loop.run_until_complete.side_effect = mock_asyncio_run_untill_complete
254-
loop, client = AsyncModbusSerialClient(schedulers.ASYNC_IO, method=method, port=SERIAL_PORT, loop=loop)
237+
loop, client = AsyncModbusSerialClient(schedulers.ASYNC_IO, method=method, port=SERIAL_PORT, loop=loop,
238+
baudrate=19200, parity='E', stopbits=2, bytesize=7)
255239
assert(isinstance(client, AsyncioModbusSerialClient))
256-
assert(len(list(client.protocol.transaction)) == 0)
257240
assert(isinstance(client.framer, framer))
258-
assert(client.protocol._connected)
259-
260-
d = client.protocol._buildResponse(0x00)
261-
262-
def handle_failure(failure):
263-
assert(isinstance(failure.exception(), ConnectionException))
264-
265-
d.add_done_callback(handle_failure)
266-
assert(client.protocol._connected)
267-
client.protocol.close()
268-
assert(not client.protocol._connected)
269-
pass
241+
assert(client.port == SERIAL_PORT)
242+
assert(client.baudrate == 19200)
243+
assert(client.parity == 'E')
244+
assert(client.stopbits == 2)
245+
assert(client.bytesize == 7)
270246

271247

272248
# ---------------------------------------------------------------------------#

0 commit comments

Comments
 (0)