Skip to content

Commit c7b89a9

Browse files
authored
Merge pull request #15 from rambo/rambo
Improve efficiency (remove busy-loops, use events instead)
2 parents c6170db + 9250644 commit c7b89a9

6 files changed

Lines changed: 81 additions & 74 deletions

File tree

poetry.lock

Lines changed: 37 additions & 51 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ branch = true
4646
[tool.poetry.dependencies]
4747
python = "^3.6"
4848
pyserial = "^3.4"
49-
async-timeout = "^3.0"
5049

5150
[tool.poetry.dev-dependencies]
5251
pytest = "^6.2"

src/scpi/scpi.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
import decimal
44
import re
55

6-
from async_timeout import timeout
7-
86
from .errors import CommandError
97
from .transports.baseclass import AbstractTransport
108

@@ -175,14 +173,19 @@ async def check_error(self, prev_command=""):
175173
async def command(self, command, cmd_timeout=COMMAND_DEFAULT_TIMEOUT, abort_on_timeout=True):
176174
"""Sends a command, does not wait for response"""
177175
try:
178-
with timeout(cmd_timeout):
176+
177+
async def _command() -> None:
178+
"""Wrap the actual work"""
179+
nonlocal self
179180
async with self.lock:
180181
await self.transport.send_command(command)
182+
183+
await asyncio.wait_for(_command(command), timeout=cmd_timeout)
181184
except (asyncio.TimeoutError, asyncio.CancelledError) as err:
182185
# check for the actual error if available
183186
await self.check_error(command)
184187
if abort_on_timeout:
185-
self.abort_command()
188+
await self.abort_command()
186189
# re-raise the timeout if no other error found
187190
raise err
188191
# other errors are allowed to bubble-up as-is
@@ -195,11 +198,14 @@ async def safe_command(self, command, *args, **kwargs):
195198
async def ask(self, command, cmd_timeout=COMMAND_DEFAULT_TIMEOUT, abort_on_timeout=True):
196199
"""Send a command and waits for response, returns the response"""
197200
try:
198-
with timeout(cmd_timeout):
201+
202+
async def _ask(command: str) -> str:
203+
"""Wrap the actual work"""
199204
async with self.lock:
200205
await self.transport.send_command(command)
201206
return await self.transport.get_response()
202207

208+
return await asyncio.wait_for(_ask(command), timeout=cmd_timeout)
203209
except (asyncio.TimeoutError, asyncio.CancelledError) as err:
204210
# check for the actual error if available
205211
await self.check_error(command)

src/scpi/transports/baseclass.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""
55
import asyncio
66
import logging
7+
import threading
78

89
logger = logging.getLogger(__name__)
910

@@ -19,6 +20,8 @@ class BaseTransport(AbstractTransport):
1920
message_callback = None
2021
unsolicited_message_callback = None
2122
lock = asyncio.Lock()
23+
aioevent = asyncio.Event()
24+
blevent = threading.Event()
2225

2326
async def quit(self):
2427
"""Must shutdown all background threads (if any)"""

src/scpi/transports/gpib/prologix.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
""""Driver" for http://prologix.biz/gpib-usb-controller.html GPIB controller"""
22
import asyncio
3+
import logging
4+
import threading
35

46
import serial
57
import serial.threaded
6-
from async_timeout import timeout
78

89
from ..rs232 import RS232SerialProtocol
910
from .base import GPIBTransport
1011

1112
SCAN_DEVICE_TIMEOUT = 0.5
1213
READ_TIMEOUT = 1.0
14+
LOGGER = logging.getLogger(__name__)
1315

1416

1517
class PrologixRS232SerialProtocol(RS232SerialProtocol):
@@ -61,30 +63,35 @@ async def get_response(self):
6163
async def send_and_read(self, send):
6264
"""Send a line, read the response. NOTE: This is for talking with the controller, device responses
6365
need to use get_response as usual"""
64-
with timeout(READ_TIMEOUT):
66+
67+
async def _send_and_read(send) -> str:
68+
"""Wrap the actual work"""
6569
async with self.lock:
6670
response = None
6771

6872
def set_response(message):
6973
"""Callback for setting the response"""
70-
nonlocal response
74+
nonlocal response, self
7175
response = message
76+
self.blevent.set()
7277

78+
self.blevent.clear()
7379
self.message_callback = set_response
7480
self.serialhandler.protocol.write_line(send)
75-
while response is None:
76-
await asyncio.sleep(0)
81+
await asyncio.get_event_loop().run_in_executor(None, self.blevent.wait)
7782
return response
7883

84+
return await asyncio.wait_for(_send_and_read(send), timeout=READ_TIMEOUT)
85+
7986
async def set_address(self, address, secondary=None):
8087
"""Set the address we want to talk to"""
8188
if secondary is None:
8289
await self.send_command("++addr %d" % address)
8390
else:
8491
await self.send_command("++addr %d %d" % (address, secondary))
85-
# Wait for the address to actually be set
92+
8693
while True:
87-
await asyncio.sleep(0)
94+
await asyncio.sleep(0.001)
8895
resp = await self.query_address()
8996
if resp == (address, secondary):
9097
break
@@ -144,13 +151,18 @@ async def scan_devices(self):
144151
prev_read_tmo_ms = await self.send_and_read("++read_tmo_ms")
145152
self.serialhandler.protocol.write_line("++read_tmo_ms %d" % int((SCAN_DEVICE_TIMEOUT / 2) * 1000))
146153
for addr in range(0, 31): # 0-30 inclusive
147-
with timeout(SCAN_DEVICE_TIMEOUT):
148-
try:
149-
await self.set_address(addr)
150-
await self.poll()
151-
found_addresses.append(addr)
152-
except (asyncio.TimeoutError, asyncio.CancelledError):
153-
pass
154+
155+
async def _scan_addr(addr) -> None:
156+
"""Sacn single address"""
157+
nonlocal found_addresses
158+
await self.set_address(addr)
159+
await self.poll()
160+
found_addresses.append(addr)
161+
162+
try:
163+
await asyncio.wait_for(_scan_addr(addr), timeout=SCAN_DEVICE_TIMEOUT)
164+
except (asyncio.TimeoutError, asyncio.CancelledError):
165+
pass
154166
self.serialhandler.protocol.write_line("++read_tmo_ms " + prev_read_tmo_ms)
155167
# Wait a moment for things to settle
156168
await asyncio.sleep(float(prev_read_tmo_ms) / 1000)

src/scpi/transports/rs232.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ async def get_response(self):
5151

5252
def set_response(message):
5353
"""Callback for setting the response"""
54-
nonlocal response
54+
nonlocal response, self
5555
response = message
56+
self.blevent.set()
5657

58+
self.blevent.clear()
5759
self.message_callback = set_response
58-
while response is None:
59-
await asyncio.sleep(0)
60+
await asyncio.get_event_loop().run_in_executor(None, self.blevent.wait)
6061
return response
6162

6263
async def abort_command(self):

0 commit comments

Comments
 (0)