forked from M-Labs/artiq
waveform: major refactor to proxy connectors
This commit is contained in:
parent
21c391865f
commit
70542b0a5b
@ -1,6 +1,7 @@
|
|||||||
from PyQt5 import QtCore, QtWidgets, QtGui
|
from PyQt5 import QtCore, QtWidgets, QtGui
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
|
from sipyco.asyncio_tools import atexit_register_coroutine
|
||||||
from sipyco.sync_struct import Subscriber
|
from sipyco.sync_struct import Subscriber
|
||||||
from sipyco.pc_rpc import AsyncioClient
|
from sipyco.pc_rpc import AsyncioClient
|
||||||
from sipyco import pyon
|
from sipyco import pyon
|
||||||
@ -88,7 +89,7 @@ class _AddChannelDialog(QtWidgets.QDialog):
|
|||||||
self.accepted.emit(channels)
|
self.accepted.emit(channels)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
# TODO: make private
|
||||||
class BackgroundItem(pg.GraphicsWidgetAnchor, pg.GraphicsWidget):
|
class BackgroundItem(pg.GraphicsWidgetAnchor, pg.GraphicsWidget):
|
||||||
def __init__(self, parent, rect):
|
def __init__(self, parent, rect):
|
||||||
pg.GraphicsWidget.__init__(self, parent)
|
pg.GraphicsWidget.__init__(self, parent)
|
||||||
@ -278,14 +279,13 @@ class BitWaveform(Waveform):
|
|||||||
display_x = []
|
display_x = []
|
||||||
previous_y = None
|
previous_y = None
|
||||||
for x, y in zip(self.x_data, self.y_data):
|
for x, y in zip(self.x_data, self.y_data):
|
||||||
state_unchanged = previous_y == y
|
if y == "X": # TODO: replace with dictionary mapping
|
||||||
if y == "X":
|
|
||||||
dis_y = DISPLAY_MID
|
dis_y = DISPLAY_MID
|
||||||
elif y == "1":
|
elif y == "1":
|
||||||
dis_y = DISPLAY_HIGH
|
dis_y = DISPLAY_HIGH
|
||||||
else:
|
else:
|
||||||
dis_y = DISPLAY_LOW
|
dis_y = DISPLAY_LOW
|
||||||
if state_unchanged:
|
if previous_y == y: # TODO: extract to separate function
|
||||||
arw = pg.ArrowItem(pxMode=True, angle=90)
|
arw = pg.ArrowItem(pxMode=True, angle=90)
|
||||||
self.addItem(arw)
|
self.addItem(arw)
|
||||||
self._arrows.append(arw)
|
self._arrows.append(arw)
|
||||||
@ -309,7 +309,7 @@ class BitVectorWaveform(Waveform):
|
|||||||
Waveform.__init__(self, channel, state, parent)
|
Waveform.__init__(self, channel, state, parent)
|
||||||
self._labels = []
|
self._labels = []
|
||||||
hx = math.ceil(self.width / 4)
|
hx = math.ceil(self.width / 4)
|
||||||
self._format_string = "{:0=" + str(hx) + "X}"
|
self._format_string = "{:0=" + str(hx) + "X}" # TODO: change method..
|
||||||
self.view_box.sigTransformChanged.connect(self._update_labels)
|
self.view_box.sigTransformChanged.connect(self._update_labels)
|
||||||
|
|
||||||
def _update_labels(self):
|
def _update_labels(self):
|
||||||
@ -339,7 +339,7 @@ class BitVectorWaveform(Waveform):
|
|||||||
for x, y in zip(self.x_data, self.y_data):
|
for x, y in zip(self.x_data, self.y_data):
|
||||||
display_x.append(x)
|
display_x.append(x)
|
||||||
display_y.append(DISPLAY_LOW)
|
display_y.append(DISPLAY_LOW)
|
||||||
if "X" in y:
|
if "X" in y: # TODO change to using a dictionary
|
||||||
display_x.append(x)
|
display_x.append(x)
|
||||||
display_y.append(DISPLAY_MID)
|
display_y.append(DISPLAY_MID)
|
||||||
elif int(y) != 0:
|
elif int(y) != 0:
|
||||||
@ -358,7 +358,7 @@ class BitVectorWaveform(Waveform):
|
|||||||
self.plot_data_item.setData(x=[], y=[])
|
self.plot_data_item.setData(x=[], y=[])
|
||||||
|
|
||||||
def format_cursor_label(self):
|
def format_cursor_label(self):
|
||||||
if "X" in self.cursor_y:
|
if "X" in self.cursor_y: # TODO: this will not happen in any current implementation of the bit vector handlers..
|
||||||
lbl = self.cursor_y
|
lbl = self.cursor_y
|
||||||
else:
|
else:
|
||||||
lbl = self._format_string.format(int(self.cursor_y, 2))
|
lbl = self._format_string.format(int(self.cursor_y, 2))
|
||||||
@ -368,14 +368,17 @@ class BitVectorWaveform(Waveform):
|
|||||||
class AnalogWaveform(Waveform):
|
class AnalogWaveform(Waveform):
|
||||||
def __init__(self, channel, state, parent=None):
|
def __init__(self, channel, state, parent=None):
|
||||||
Waveform.__init__(self, channel, state, parent)
|
Waveform.__init__(self, channel, state, parent)
|
||||||
self.plot_data_item.setDownsampling(ds=10, method="peak", auto=True)
|
self.plot_data_item.setDownsampling(ds=10, method="peak", auto=True)
|
||||||
|
# TODO: experiment with downsampling values for best performance
|
||||||
|
# TODO: potentially switch to not using a step connect
|
||||||
|
|
||||||
def extract_data_from_state(self):
|
def extract_data_from_state(self):
|
||||||
try:
|
try:
|
||||||
self.x_data, self.y_data = zip(*self.state['data'][self.name])
|
self.x_data, self.y_data = zip(*self.state['data'][self.name])
|
||||||
except:
|
except:
|
||||||
logger.debug('Error caught when loading waveform data: {}'.format(self.name), exc_info=True)
|
logger.debug('Error caught when loading waveform data: {}'.format(self.name), exc_info=True)
|
||||||
|
|
||||||
|
# TODO: change to using max_, min_
|
||||||
def display(self):
|
def display(self):
|
||||||
try:
|
try:
|
||||||
self.plot_data_item.setData(x=self.x_data, y=self.y_data)
|
self.plot_data_item.setData(x=self.x_data, y=self.y_data)
|
||||||
@ -386,8 +389,9 @@ class AnalogWaveform(Waveform):
|
|||||||
logger.debug('Error caught when displaying waveform: {}'.format(self.name), exc_info=True)
|
logger.debug('Error caught when displaying waveform: {}'.format(self.name), exc_info=True)
|
||||||
self.plot_data_item.setData(x=[0], y=[0])
|
self.plot_data_item.setData(x=[0], y=[0])
|
||||||
|
|
||||||
|
# TODO: can also just leave it as None -> simpler
|
||||||
def format_cursor_label(self):
|
def format_cursor_label(self):
|
||||||
if self.cursor_y is None:
|
if self.cursor_y is None:
|
||||||
lbl = "nan"
|
lbl = "nan"
|
||||||
else:
|
else:
|
||||||
lbl = str(self.cursor_y)
|
lbl = str(self.cursor_y)
|
||||||
@ -433,6 +437,7 @@ class WaveformArea(QtWidgets.QWidget):
|
|||||||
scroll_area.setFrameShape(QtWidgets.QFrame.NoFrame)
|
scroll_area.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||||
layout.addWidget(scroll_area)
|
layout.addWidget(scroll_area)
|
||||||
|
|
||||||
|
# TODO: name changed to VDragDropSplitter
|
||||||
self._splitter = DragDropSplitter(parent=scroll_area)
|
self._splitter = DragDropSplitter(parent=scroll_area)
|
||||||
self._splitter.setHandleWidth(1)
|
self._splitter.setHandleWidth(1)
|
||||||
scroll_area.setWidget(self._splitter)
|
scroll_area.setWidget(self._splitter)
|
||||||
@ -518,7 +523,7 @@ class WaveformArea(QtWidgets.QWidget):
|
|||||||
cw.display()
|
cw.display()
|
||||||
cw.on_cursor_move(self._cursor_x_pos)
|
cw.on_cursor_move(self._cursor_x_pos)
|
||||||
cw.update_x_max()
|
cw.update_x_max()
|
||||||
maximum = self._state["stopped_x"]
|
maximum = self._state["stopped_x"] # TODO: change maximum -> stopped_x
|
||||||
self._ref_axis.setLimits(xMax=maximum)
|
self._ref_axis.setLimits(xMax=maximum)
|
||||||
if maximum is not None:
|
if maximum is not None:
|
||||||
self._ref_axis.setRange(xRange=(0, maximum))
|
self._ref_axis.setRange(xRange=(0, maximum))
|
||||||
@ -536,102 +541,7 @@ class WaveformArea(QtWidgets.QWidget):
|
|||||||
cw.set_cursor_visible(self._cursor_visible)
|
cw.set_cursor_visible(self._cursor_visible)
|
||||||
|
|
||||||
|
|
||||||
class WaveformProxyClient:
|
# TODO: move elsewhere -> this can be a late addition
|
||||||
def __init__(self, state, loop):
|
|
||||||
self._state = state
|
|
||||||
self._loop = loop
|
|
||||||
|
|
||||||
self.devices_sub = None
|
|
||||||
self.rpc_client = AsyncioClient()
|
|
||||||
self.proxy_receiver = None
|
|
||||||
|
|
||||||
self._proxy_addr = None
|
|
||||||
self._proxy_port = None
|
|
||||||
self._proxy_port_ctl = None
|
|
||||||
self._on_sub_reconnect = asyncio.Event()
|
|
||||||
self._on_rpc_reconnect = asyncio.Event()
|
|
||||||
self._reconnect_rpc_task = None
|
|
||||||
self._reconnect_receiver_task = None
|
|
||||||
|
|
||||||
async def trigger_proxy_task(self):
|
|
||||||
try:
|
|
||||||
if self.rpc_client.get_rpc_id()[0] is None:
|
|
||||||
raise AttributeError("Unable to identify RPC target. Is analyzer proxy connected?")
|
|
||||||
asyncio.ensure_future(self.rpc_client.trigger())
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning("Failed to pull from device: %s", e)
|
|
||||||
|
|
||||||
def update_address(self, addr, port, port_control):
|
|
||||||
self._proxy_addr = addr
|
|
||||||
self._proxy_port = port
|
|
||||||
self._proxy_port_ctl = port_control
|
|
||||||
self._on_rpc_reconnect.set()
|
|
||||||
self._on_sub_reconnect.set()
|
|
||||||
|
|
||||||
# Proxy client connections
|
|
||||||
async def start(self, server, port):
|
|
||||||
try:
|
|
||||||
await self.devices_sub.connect(server, port)
|
|
||||||
self._reconnect_rpc_task = asyncio.ensure_future(
|
|
||||||
self.reconnect_rpc(), loop=self._loop)
|
|
||||||
self._reconnect_receiver_task = asyncio.ensure_future(
|
|
||||||
self.reconnect_receiver(), loop=self._loop)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Failed to connect to master: %s", e)
|
|
||||||
|
|
||||||
async def reconnect_rpc(self):
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
await self._on_rpc_reconnect.wait()
|
|
||||||
self._on_rpc_reconnect.clear()
|
|
||||||
logger.info("Attempting analyzer proxy RPC connection...")
|
|
||||||
try:
|
|
||||||
await self.rpc_client.connect_rpc(self._proxy_addr,
|
|
||||||
self._proxy_port_ctl,
|
|
||||||
"coreanalyzer_proxy_control")
|
|
||||||
except Exception:
|
|
||||||
logger.info("Analyzer proxy RPC timed out, trying again...")
|
|
||||||
await asyncio.sleep(5)
|
|
||||||
self._on_rpc_reconnect.set()
|
|
||||||
else:
|
|
||||||
logger.info("RPC connected to analyzer proxy on %s/%s",
|
|
||||||
self._proxy_addr, self._proxy_port_ctl)
|
|
||||||
except asyncio.CancelledError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def reconnect_receiver(self):
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
await self._on_sub_reconnect.wait()
|
|
||||||
self._on_sub_reconnect.clear()
|
|
||||||
logger.info("Setting up analyzer proxy receiver...")
|
|
||||||
try:
|
|
||||||
await self.proxy_receiver.connect(
|
|
||||||
self._proxy_addr, self._proxy_port)
|
|
||||||
except Exception:
|
|
||||||
logger.info("Failed to set up analyzer proxy receiver, reconnecting...")
|
|
||||||
await asyncio.sleep(5)
|
|
||||||
self._on_sub_reconnect.set()
|
|
||||||
else:
|
|
||||||
logger.info("Receiving from analyzer proxy on %s:%s",
|
|
||||||
self._proxy_addr, self._proxy_port)
|
|
||||||
except asyncio.CancelledError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def stop(self):
|
|
||||||
try:
|
|
||||||
self._reconnect_rpc_task.cancel()
|
|
||||||
self._reconnect_receiver_task.cancel()
|
|
||||||
await asyncio.wait_for(self._reconnect_rpc_task, None)
|
|
||||||
await asyncio.wait_for(self._reconnect_receiver_task, None)
|
|
||||||
await self.devices_sub.close()
|
|
||||||
self.rpc_client.close_rpc()
|
|
||||||
await self.proxy_receiver.close()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Error occurred while closing proxy connections: %s",
|
|
||||||
e, exc_info=True)
|
|
||||||
|
|
||||||
|
|
||||||
class _CursorTimeControl(QtWidgets.QLineEdit):
|
class _CursorTimeControl(QtWidgets.QLineEdit):
|
||||||
submit = QtCore.pyqtSignal(float)
|
submit = QtCore.pyqtSignal(float)
|
||||||
PRECISION = 15
|
PRECISION = 15
|
||||||
@ -663,6 +573,80 @@ class _CursorTimeControl(QtWidgets.QLineEdit):
|
|||||||
self.clearFocus()
|
self.clearFocus()
|
||||||
|
|
||||||
|
|
||||||
|
class _BaseProxyClient:
|
||||||
|
def __init__(self):
|
||||||
|
self.addr = None
|
||||||
|
self.port = None
|
||||||
|
self._reconnect_event = asyncio.Event()
|
||||||
|
self._reconnect_task = None
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
self._reconnect_task = asyncio.ensure_future(
|
||||||
|
exc_to_warning(self._reconnect()))
|
||||||
|
|
||||||
|
def update_address(self, addr, port):
|
||||||
|
self.addr = addr
|
||||||
|
self.port = port
|
||||||
|
self._reconnect_event.set()
|
||||||
|
|
||||||
|
async def _reconnect(self):
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
await self._reconnect_event.wait()
|
||||||
|
self._reconnect_event.clear()
|
||||||
|
try:
|
||||||
|
await self.reconnect_cr()
|
||||||
|
except Exception:
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
self._reconnect_event.set()
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
try:
|
||||||
|
self._reconnect_task.cancel()
|
||||||
|
await asyncio.wait_for(self._reconnect_task, None)
|
||||||
|
await self.disconnect_cr()
|
||||||
|
except:
|
||||||
|
logger.error("Error caught while closing proxy client.", exc_info=True)
|
||||||
|
|
||||||
|
async def reconnect_cr(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def disconnect_cr(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class RPCProxyClient(_BaseProxyClient):
|
||||||
|
def __init__(self):
|
||||||
|
_BaseProxyClient.__init__(self)
|
||||||
|
self.client = AsyncioClient()
|
||||||
|
|
||||||
|
async def trigger_proxy_task(self):
|
||||||
|
if self.client.get_rpc_id()[0] is None:
|
||||||
|
raise AttributeError("Unable to identify RPC target. Is analyzer proxy connected?")
|
||||||
|
await self.client.trigger()
|
||||||
|
|
||||||
|
async def reconnect_cr(self):
|
||||||
|
await self.client.connect_rpc(self.addr,
|
||||||
|
self.port,
|
||||||
|
"coreanalyzer_proxy_control")
|
||||||
|
|
||||||
|
async def disconnect_cr(self):
|
||||||
|
self.client.close_rpc()
|
||||||
|
|
||||||
|
|
||||||
|
class ReceiverProxyClient(_BaseProxyClient):
|
||||||
|
def __init__(self, receiver):
|
||||||
|
_BaseProxyClient.__init__(self)
|
||||||
|
self.receiver = receiver
|
||||||
|
|
||||||
|
async def reconnect_cr(self):
|
||||||
|
await self.receiver.connect(self.addr, self.port)
|
||||||
|
|
||||||
|
async def disconnect_cr(self):
|
||||||
|
await self.receiver.close()
|
||||||
|
|
||||||
class WaveformDock(QtWidgets.QDockWidget):
|
class WaveformDock(QtWidgets.QDockWidget):
|
||||||
traceDataChanged = QtCore.pyqtSignal()
|
traceDataChanged = QtCore.pyqtSignal()
|
||||||
|
|
||||||
@ -685,14 +669,12 @@ class WaveformDock(QtWidgets.QDockWidget):
|
|||||||
}
|
}
|
||||||
|
|
||||||
self._current_dir = os.getcwd()
|
self._current_dir = os.getcwd()
|
||||||
|
|
||||||
self.proxy_client = WaveformProxyClient(self._state, loop)
|
self.devices_sub = Subscriber("devices", self.init_ddb, self.update_ddb)
|
||||||
devices_sub = Subscriber("devices", self.init_ddb, self.update_ddb)
|
self.rpc_client = RPCProxyClient()
|
||||||
|
receiver = comm_analyzer.AnalyzerProxyReceiver(
|
||||||
proxy_receiver = comm_analyzer.AnalyzerProxyReceiver(
|
|
||||||
self.on_dump_receive)
|
self.on_dump_receive)
|
||||||
self.proxy_client.devices_sub = devices_sub
|
self.receiver_client = ReceiverProxyClient(receiver)
|
||||||
self.proxy_client.proxy_receiver = proxy_receiver
|
|
||||||
|
|
||||||
grid = LayoutWidget()
|
grid = LayoutWidget()
|
||||||
self.setWidget(grid)
|
self.setWidget(grid)
|
||||||
@ -710,7 +692,7 @@ class WaveformDock(QtWidgets.QDockWidget):
|
|||||||
QtWidgets.QStyle.SP_BrowserReload))
|
QtWidgets.QStyle.SP_BrowserReload))
|
||||||
grid.addWidget(self._request_dump_btn, 0, 1)
|
grid.addWidget(self._request_dump_btn, 0, 1)
|
||||||
self._request_dump_btn.clicked.connect(
|
self._request_dump_btn.clicked.connect(
|
||||||
lambda: asyncio.ensure_future(self.proxy_client.trigger_proxy_task()))
|
lambda: asyncio.ensure_future(self.rpc_client.trigger_proxy_task()))
|
||||||
|
|
||||||
self._waveform_area = WaveformArea(self, self._state,
|
self._waveform_area = WaveformArea(self, self._state,
|
||||||
self._channel_model)
|
self._channel_model)
|
||||||
@ -748,7 +730,7 @@ class WaveformDock(QtWidgets.QDockWidget):
|
|||||||
|
|
||||||
def _update_log_channels(self):
|
def _update_log_channels(self):
|
||||||
for log in self._state['logs']:
|
for log in self._state['logs']:
|
||||||
self._channel_model[log] = (0, "log")
|
self._channel_model[log] = (0, WaveformType.LOG)
|
||||||
|
|
||||||
def on_dump_receive(self, data):
|
def on_dump_receive(self, data):
|
||||||
decoded_dump = comm_analyzer.decode_dump(data)
|
decoded_dump = comm_analyzer.decode_dump(data)
|
||||||
@ -858,7 +840,8 @@ class WaveformDock(QtWidgets.QDockWidget):
|
|||||||
addr = desc["host"]
|
addr = desc["host"]
|
||||||
port = desc.get("port_proxy", 1385)
|
port = desc.get("port_proxy", 1385)
|
||||||
port_control = desc.get("port_proxy_control", 1386)
|
port_control = desc.get("port_proxy_control", 1386)
|
||||||
self.proxy_client.update_address(addr, port, port_control)
|
self.rpc_client.update_address(addr, port_control)
|
||||||
|
self.receiver_client.update_address(addr, port)
|
||||||
|
|
||||||
def init_ddb(self, ddb):
|
def init_ddb(self, ddb):
|
||||||
logger.info("init ddb")
|
logger.info("init ddb")
|
||||||
|
Loading…
Reference in New Issue
Block a user