forked from M-Labs/artiq
applets: clean shutdown
This commit is contained in:
parent
8be0696b39
commit
8844fba4c9
|
@ -1,3 +1,4 @@
|
||||||
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
@ -8,7 +9,13 @@ from artiq.protocols import pyon
|
||||||
from artiq.protocols.pipe_ipc import AsyncioChildComm
|
from artiq.protocols.pipe_ipc import AsyncioChildComm
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AppletIPCClient(AsyncioChildComm):
|
class AppletIPCClient(AsyncioChildComm):
|
||||||
|
def set_close_cb(self, close_cb):
|
||||||
|
self.close_cb = close_cb
|
||||||
|
|
||||||
def write_pyon(self, obj):
|
def write_pyon(self, obj):
|
||||||
self.write(pyon.encode(obj).encode() + b"\n")
|
self.write(pyon.encode(obj).encode() + b"\n")
|
||||||
|
|
||||||
|
@ -17,12 +24,37 @@ class AppletIPCClient(AsyncioChildComm):
|
||||||
return pyon.decode(line.decode())
|
return pyon.decode(line.decode())
|
||||||
|
|
||||||
async def embed(self, win_id):
|
async def embed(self, win_id):
|
||||||
|
# This function is only called when not subscribed to anything,
|
||||||
|
# so the only normal replies are embed_done and terminate.
|
||||||
self.write_pyon({"action": "embed",
|
self.write_pyon({"action": "embed",
|
||||||
"win_id": win_id})
|
"win_id": win_id})
|
||||||
reply = await self.read_pyon()
|
reply = await self.read_pyon()
|
||||||
if reply["action"] != "embed_done":
|
if reply["action"] == "terminate":
|
||||||
raise ValueError("Got erroneous reply to embed request",
|
self.close_cb()
|
||||||
reply)
|
elif reply["action"] != "embed_done":
|
||||||
|
logger.error("unexpected action reply to embed request: %s",
|
||||||
|
action)
|
||||||
|
self.close_cb()
|
||||||
|
|
||||||
|
async def listen(self):
|
||||||
|
while True:
|
||||||
|
obj = await self.read_pyon()
|
||||||
|
try:
|
||||||
|
action = obj["action"]
|
||||||
|
if action == "terminate":
|
||||||
|
self.close_cb()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise ValueError("unknown action in applet request")
|
||||||
|
except:
|
||||||
|
logger.error("error processing applet request",
|
||||||
|
exc_info=True)
|
||||||
|
self.close_cb()
|
||||||
|
|
||||||
|
def subscribe(self, datasets):
|
||||||
|
self.write_pyon({"action": "subscribe",
|
||||||
|
"datasets": datasets})
|
||||||
|
asyncio.ensure_future(self.listen())
|
||||||
|
|
||||||
|
|
||||||
class SimpleApplet:
|
class SimpleApplet:
|
||||||
|
@ -108,6 +140,7 @@ class SimpleApplet:
|
||||||
# Doing embedding the other way around (using QWindow.setParent in the
|
# Doing embedding the other way around (using QWindow.setParent in the
|
||||||
# applet) breaks resizing.
|
# applet) breaks resizing.
|
||||||
if self.args.mode == "embedded":
|
if self.args.mode == "embedded":
|
||||||
|
self.ipc.set_close_cb(self.main_widget.close)
|
||||||
win_id = int(self.main_widget.winId())
|
win_id = int(self.main_widget.winId())
|
||||||
self.loop.run_until_complete(self.ipc.embed(win_id))
|
self.loop.run_until_complete(self.ipc.embed(win_id))
|
||||||
self.main_widget.show()
|
self.main_widget.show()
|
||||||
|
@ -155,8 +188,7 @@ class SimpleApplet:
|
||||||
self.loop.run_until_complete(self.subscriber.connect(
|
self.loop.run_until_complete(self.subscriber.connect(
|
||||||
self.args.server_notify, self.args.port_notify))
|
self.args.server_notify, self.args.port_notify))
|
||||||
elif self.args.mode == "embedded":
|
elif self.args.mode == "embedded":
|
||||||
# TODO
|
self.ipc.subscribe(self.datasets)
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,14 @@ from functools import partial
|
||||||
from quamash import QtCore, QtGui, QtWidgets
|
from quamash import QtCore, QtGui, QtWidgets
|
||||||
from pyqtgraph import dockarea
|
from pyqtgraph import dockarea
|
||||||
|
|
||||||
from artiq.protocols import pyon
|
|
||||||
from artiq.protocols.pipe_ipc import AsyncioParentComm
|
from artiq.protocols.pipe_ipc import AsyncioParentComm
|
||||||
|
from artiq.protocols import pyon
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AppletIPCServer(AsyncioParentComm):
|
class AppletIPCServer(AsyncioParentComm):
|
||||||
def __init__(self, capture_cb):
|
|
||||||
AsyncioParentComm.__init__(self)
|
|
||||||
self.capture_cb = capture_cb
|
|
||||||
|
|
||||||
def write_pyon(self, obj):
|
def write_pyon(self, obj):
|
||||||
self.write(pyon.encode(obj).encode() + b"\n")
|
self.write(pyon.encode(obj).encode() + b"\n")
|
||||||
|
|
||||||
|
@ -26,25 +22,40 @@ class AppletIPCServer(AsyncioParentComm):
|
||||||
line = await self.readline()
|
line = await self.readline()
|
||||||
return pyon.decode(line.decode())
|
return pyon.decode(line.decode())
|
||||||
|
|
||||||
async def serve(self):
|
async def serve(self, embed_cb):
|
||||||
while True:
|
try:
|
||||||
obj = await self.read_pyon()
|
while True:
|
||||||
try:
|
obj = await self.read_pyon()
|
||||||
action = obj["action"]
|
try:
|
||||||
if action == "embed":
|
action = obj["action"]
|
||||||
self.capture_cb(obj["win_id"])
|
if action == "embed":
|
||||||
self.write_pyon({"action": "embed_done"})
|
embed_cb(obj["win_id"])
|
||||||
else:
|
self.write_pyon({"action": "embed_done"})
|
||||||
raise ValueError("unknown action in applet request")
|
elif action == "subscribe":
|
||||||
except:
|
print("applet subscribed: ", obj["datasets"])
|
||||||
logger.warning("error processing applet request",
|
else:
|
||||||
exc_info=True)
|
raise ValueError("unknown action in applet request")
|
||||||
self.write_pyon({"action": "error"})
|
except:
|
||||||
|
logger.warning("error processing applet request",
|
||||||
|
exc_info=True)
|
||||||
|
self.write_pyon({"action": "error"})
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
logger.error("error processing data from applet, "
|
||||||
|
"server stopped", exc_info=True)
|
||||||
|
|
||||||
|
def start(self, embed_cb):
|
||||||
|
self.server_task = asyncio.ensure_future(self.serve(embed_cb))
|
||||||
|
|
||||||
|
async def stop(self):
|
||||||
|
self.server_task.cancel()
|
||||||
|
await asyncio.wait([self.server_task])
|
||||||
|
|
||||||
|
|
||||||
class AppletDock(dockarea.Dock):
|
class AppletDock(dockarea.Dock):
|
||||||
def __init__(self, name, command):
|
def __init__(self, name, command):
|
||||||
dockarea.Dock.__init__(self, "applet" + str(id(self)), # XXX
|
dockarea.Dock.__init__(self, "applet" + str(id(self)), # TODO
|
||||||
label="Applet: " + name,
|
label="Applet: " + name,
|
||||||
closable=True)
|
closable=True)
|
||||||
self.setMinimumSize(QtCore.QSize(500, 400))
|
self.setMinimumSize(QtCore.QSize(500, 400))
|
||||||
|
@ -56,7 +67,7 @@ class AppletDock(dockarea.Dock):
|
||||||
self.label.setText("Applet: " + name)
|
self.label.setText("Applet: " + name)
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
self.ipc = AppletIPCServer(self.capture)
|
self.ipc = AppletIPCServer()
|
||||||
command = self.command.format(python=sys.executable,
|
command = self.command.format(python=sys.executable,
|
||||||
ipc_address=self.ipc.get_address())
|
ipc_address=self.ipc.get_address())
|
||||||
logger.debug("starting command %s for %s", command, self.applet_name)
|
logger.debug("starting command %s for %s", command, self.applet_name)
|
||||||
|
@ -65,18 +76,18 @@ class AppletDock(dockarea.Dock):
|
||||||
except:
|
except:
|
||||||
logger.warning("Applet %s failed to start", self.applet_name,
|
logger.warning("Applet %s failed to start", self.applet_name,
|
||||||
exc_info=True)
|
exc_info=True)
|
||||||
asyncio.ensure_future(self.ipc.serve())
|
self.ipc.start(self.embed)
|
||||||
|
|
||||||
def capture(self, win_id):
|
def embed(self, win_id):
|
||||||
logger.debug("capturing window 0x%x for %s", win_id, self.applet_name)
|
logger.debug("capturing window 0x%x for %s", win_id, self.applet_name)
|
||||||
captured_window = QtGui.QWindow.fromWinId(win_id)
|
embed_window = QtGui.QWindow.fromWinId(win_id)
|
||||||
captured_widget = QtWidgets.QWidget.createWindowContainer(
|
embed_widget = QtWidgets.QWidget.createWindowContainer(embed_window)
|
||||||
captured_window)
|
self.addWidget(embed_widget)
|
||||||
self.addWidget(captured_widget)
|
|
||||||
|
|
||||||
async def terminate(self):
|
async def terminate(self):
|
||||||
if hasattr(self, "process"):
|
if hasattr(self, "ipc"):
|
||||||
# TODO: send IPC termination request
|
await self.ipc.stop()
|
||||||
|
self.ipc.write_pyon({"action": "terminate"})
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(self.ipc.process.wait(), 2.0)
|
await asyncio.wait_for(self.ipc.process.wait(), 2.0)
|
||||||
except:
|
except:
|
||||||
|
|
Loading…
Reference in New Issue