Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 53b259b9a0c0
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 59194176a942
Choose a head ref
  • 2 commits
  • 11 files changed
  • 1 contributor

Commits on Oct 5, 2014

  1. Copy the full SHA
    2f58cf6 View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    Mic92 Jörg Thalheim
    Copy the full SHA
    5919417 View commit details
26 changes: 26 additions & 0 deletions artiq/management/scheduler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import asyncio

from artiq.management.worker import Worker


class Scheduler:
def __init__(self, loop):
self.loop = loop
self.queue = asyncio.Queue()

def __enter__(self):
self.worker = Worker(self.loop)
return self

def __exit__(self, type, value, traceback):
self.loop.run_until_complete(self.worker.end_process())
del self.worker

def add_run_once(self, item, timeout):
yield from self.queue.put((item, timeout))

def task(self):
while True:
item, timeout = yield from self.queue.get()
result = yield from self.worker.run(item, timeout)
print(result)
74 changes: 74 additions & 0 deletions artiq/management/worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import sys
import asyncio
import subprocess
import signal
import json


class WorkerFailed(Exception):
pass


class Worker:
def __init__(self, loop, send_timeout=0.5, start_reply_timeout=1.0,
term_timeout=1.0):
self.send_timeout = send_timeout
self.start_reply_timeout = start_reply_timeout
self.term_timeout = term_timeout
loop.run_until_complete(self.create_process())

@asyncio.coroutine
def create_process(self):
self.process = yield from asyncio.create_subprocess_exec(
sys.executable, "-m", "artiq.management.worker_impl",
stdout=subprocess.PIPE, stdin=subprocess.PIPE)

@asyncio.coroutine
def _send(self, obj, timeout):
line = json.dumps(obj)
self.process.stdin.write(line.encode())
self.process.stdin.write("\n".encode())
try:
fut = self.process.stdin.drain()
if fut is not (): # FIXME: why does Python return this?
yield from asyncio.wait_for(fut, timeout=timeout)
except asyncio.TimeoutError:
raise WorkerFailed("Timeout sending data from worker")
except:
raise WorkerFailed("Failed to send data to worker")

@asyncio.coroutine
def _recv(self, timeout):
try:
line = yield from asyncio.wait_for(
self.process.stdout.readline(), timeout=timeout)
except asyncio.TimeoutError:
raise WorkerFailed("Timeout receiving data from worker")
if not line:
raise WorkerFailed(
"Worker ended unexpectedly while trying to receive data")
try:
obj = json.loads(line.decode())
except:
raise WorkerFailed("Worker sent invalid JSON data")
return obj

@asyncio.coroutine
def run(self, run_params, result_timeout):
yield from self._send(run_params, self.send_timeout)
obj = yield from self._recv(self.start_reply_timeout)
if obj != "ack":
raise WorkerFailed("Incorrect acknowledgement")
result = yield from self._recv(result_timeout)
return result

@asyncio.coroutine
def end_process(self):
if self.process.returncode is not None:
return
self.process.send_signal(signal.SIGTERM)
try:
yield from asyncio.wait_for(
self.process.wait(), timeout=self.term_timeout)
except asyncio.TimeoutError:
self.process.send_signal(signal.SIGKILL)
45 changes: 45 additions & 0 deletions artiq/management/worker_impl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import json
import sys
import importlib


def import_in_folder(path, name):
try:
del sys.modules[name] # force path search
except KeyError:
pass
loader = importlib.find_loader(name, [path])
if loader is None:
raise ImportError("Could not find loader")
return loader.load_module()


def run(path, name):
module = import_in_folder(path, name)
module.main()


def put_object(obj):
ds = json.dumps(obj)
sys.__stdout__.write(ds)
sys.__stdout__.write("\n")
sys.__stdout__.flush()


def main():
sys.stdout = sys.stderr

while True:
line = sys.__stdin__.readline()
obj = json.loads(line)
put_object("ack")

try:
run(**obj)
except Exception as e:
put_object({"status": "failed", "message": str(e)})
else:
put_object({"status": "ok"})

if __name__ == "__main__":
main()
5 changes: 4 additions & 1 deletion examples/al_spectroscopy.py
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ def run(self):
return state_0_count


if __name__ == "__main__":
def main():
from artiq.sim import devices as sd
from artiq.sim import time

@@ -51,3 +51,6 @@ def run(self):
)
exp.run()
print(time.manager.format_timeline())

if __name__ == "__main__":
main()
5 changes: 4 additions & 1 deletion examples/compiler_test.py
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ def run(self, n, t2):
self.print_done()


if __name__ == "__main__":
def main():
from artiq.devices import corecom_dummy, core, dds_core

coredev = core.Core(corecom_dummy.CoreCom())
@@ -45,3 +45,6 @@ def run(self, n, t2):
reg_channel=3, rtio_channel=3)
)
exp.run(3, 100*us)

if __name__ == "__main__":
main()
5 changes: 4 additions & 1 deletion examples/dds_test.py
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ def run(self):
self.led.set(0)


if __name__ == "__main__":
def main():
with corecom_serial.CoreCom() as com:
coredev = core.Core(com)
exp = DDSTest(
@@ -38,3 +38,6 @@ def run(self):
led=gpio_core.GPIOOut(core=coredev, channel=1)
)
exp.run()

if __name__ == "__main__":
main()
5 changes: 4 additions & 1 deletion examples/mandelbrot.py
Original file line number Diff line number Diff line change
@@ -38,7 +38,10 @@ def run(self):
self.row()


if __name__ == "__main__":
def main():
with corecom_serial.CoreCom() as com:
exp = Mandelbrot(core=core.Core(com))
exp.run()

if __name__ == "__main__":
main()
5 changes: 4 additions & 1 deletion examples/photon_histogram.py
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ def run(self):
self.report(i, hist[i])


if __name__ == "__main__":
def main():
with corecom_serial.CoreCom() as com:
coredev = core.Core(com)
exp = PhotonHistogram(
@@ -49,3 +49,6 @@ def run(self):
nbins=100
)
exp.run()

if __name__ == "__main__":
main()
5 changes: 4 additions & 1 deletion examples/simple_simulation.py
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ def run(self):
self.d.pulse(400*MHz, 20*us)


if __name__ == "__main__":
def main():
from artiq.sim import devices as sd
from artiq.sim import time

@@ -28,3 +28,6 @@ def run(self):
)
exp.run()
print(time.manager.format_timeline())

if __name__ == "__main__":
main()
45 changes: 45 additions & 0 deletions frontend/artiq
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3

import argparse
import socket
import json


def _get_args():
parser = argparse.ArgumentParser(description="ARTIQ client")
parser.add_argument(
"-o", "--run-once", default=[], nargs=3,
action="append",
help="run experiment once. arguments: <path> <name> <timeout>")
parser.add_argument(
"-q", "--quit-master", default=False,
action="store_true",
help="causes the master to quit")
return parser.parse_args()


def _send_obj(sock, obj):
line = json.dumps(obj) + "\n"
sock.sendall(line.encode())


def main():
args = _get_args()

with socket.create_connection(("::1", 8888)) as sock:
for path, name, timeout in args.run_once:
obj = {
"action": "run_once",
"run_params": {
"path": path,
"name": name
},
"timeout": timeout
}
_send_obj(sock, obj)
if args.quit_master:
obj = {"action": "quit"}
_send_obj(sock, obj)

if __name__ == "__main__":
main()
54 changes: 54 additions & 0 deletions frontend/artiqd
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3

import asyncio
import json

from artiq.management.scheduler import Scheduler


class Server:
def __init__(self, loop, scheduler):
self.scheduler = scheduler
self.terminate_notify = asyncio.Semaphore(0)
loop.run_until_complete(
asyncio.start_server(self.handle_connection, "::1", 8888))

@asyncio.coroutine
def handle_connection(self, reader, writer):
while True:
line = yield from reader.readline()
if not line:
writer.close()
return
obj = json.loads(line.decode())
action = obj["action"]
if action == "run_once":
yield from self.scheduler.add_run_once(obj["run_params"],
float(obj["timeout"]))
elif action == "quit":
self.terminate_notify.release()
else:
print("warning: unknown action " + action)

@asyncio.coroutine
def task(self, scheduler_task):
yield from self.terminate_notify.acquire()
scheduler_task.cancel()


def main():
loop = asyncio.get_event_loop()
try:
with Scheduler(loop) as scheduler:
server = Server(loop, scheduler)
scheduler_task = asyncio.Task(scheduler.task())
server_task = asyncio.Task(server.task(scheduler_task))
loop.run_until_complete(asyncio.wait([
scheduler_task,
server_task
]))
finally:
loop.close()

if __name__ == "__main__":
main()