Skip to content

Commit b53b970

Browse files
committed
Merged in monashspinor/labscript_devices/RFBlaster_kloned (pull request labscript-suite#30)
RFBlaster stability improvements
2 parents 68d1a3f + b6c07fe commit b53b970

File tree

1 file changed

+66
-15
lines changed

1 file changed

+66
-15
lines changed

RFBlaster.py

+66-15
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
from labscript_devices import BLACS_tab, runviewer_parser
2323

24+
from labscript_utils.setup_logging import setup_logging
25+
2426
# Define a RFBlasterPseudoclock that only accepts one child clockline
2527
class RFBlasterPseudoclock(Pseudoclock):
2628
def add_device(self, device):
@@ -261,7 +263,7 @@ def initialise_GUI(self):
261263
self.address = "http://" + str(self.BLACS_connection) + ":8080"
262264

263265
# Create and set the primary worker
264-
self.create_worker("main_worker",RFBlasterWorker,{'address':self.address, 'num_DDS':self.num_DDS})
266+
self.create_worker("main_worker", RFBlasterWorker, {'address': self.address, 'num_DDS': self.num_DDS})
265267
self.primary_worker = "main_worker"
266268

267269
# Set the capabilities of this device
@@ -369,13 +371,39 @@ class RFBlasterWorker(Worker):
369371
def init(self):
370372
exec('from numpy import *', globals())
371373
global h5py; import labscript_utils.h5_lock, h5py
372-
self.timeout = 30 #How long do we wait until we assume that the RFBlaster is dead? (in seconds)
374+
global re; import re
375+
self.timeout = 10 # How long do we wait until we assume that the RFBlaster is dead? (in seconds)
376+
self.retries = 3 # Retry attempts before (a) giving up, or (b) attempting to restart kloned (uniform timeout)
377+
p = re.compile('http://([0-9.]+):[0-9]+')
378+
m = p.match(self.address)
379+
self.ip = m.group(1)
380+
# self.ip = self.BLACS_connection
381+
self.netlogger = setup_logging('rfBlaster_%s' % self.ip)
382+
self.netlogger.info('init: Started logging')
373383

374384
# See if the RFBlaster answers
375385
self.http_request()
376386

377387
self._last_program_manual_values = {}
378-
388+
389+
def restart_kloned(self, respawn_netcat=True):
390+
import socket, time
391+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
392+
s.settimeout(self.timeout)
393+
self.netlogger.info('restart_kloned: Connecting to %s...' % self.ip)
394+
s.connect((self.ip, 8009))
395+
self.netlogger.info('restart_kloned: Connected!')
396+
if respawn_netcat:
397+
self.netlogger.info('restart_kloned: Respawning netcat...')
398+
s.sendall(b'nohup nc -l -p 8009 -e /bin/sh &')
399+
time.sleep(0.5)
400+
self.netlogger.info('restart_kloned: Trying to start/restart kloned...')
401+
s.sendall(b'./startup/klone_start.sh')
402+
time.sleep(0.5)
403+
s.shutdown(socket.SHUT_WR)
404+
self.netlogger.info('restart_kloned: Finished. Closing socket.')
405+
s.close()
406+
379407
def program_manual(self,values):
380408
self._last_program_manual_values = values
381409

@@ -395,7 +423,7 @@ def program_manual(self,values):
395423
def transition_to_buffered(self,device_name,h5file,initial_values,fresh):
396424
with h5py.File(h5file,'r') as hdf5_file:
397425
group = hdf5_file['devices'][device_name]
398-
#Strip out the binary files and submit to the webserver
426+
# Strip out the binary files and submit to the webserver
399427
form = MultiPartForm()
400428
self.final_values = {}
401429
finalfreq = zeros(self.num_DDS)
@@ -410,9 +438,9 @@ def transition_to_buffered(self,device_name,h5file,initial_values,fresh):
410438
'gate':True
411439
}
412440
data = group['BINARY_CODE/DDS%d'%i].value
413-
form.add_file_content("pulse_ch%d"%i,"output_ch%d.bin"%i,data)
441+
form.add_file_content("pulse_ch%d"%i, "output_ch%d.bin"%i, data)
414442

415-
form.add_field("upload_and_run","Upload and start")
443+
form.add_field("upload_and_run", "Upload and start")
416444
self.http_request(form)
417445
return self.final_values
418446

@@ -426,8 +454,8 @@ def abort_transition_to_buffered(self):
426454

427455
def abort_buffered(self):
428456
form = MultiPartForm()
429-
#tell the rfblaster to stop
430-
form.add_field("halt","Halt execution")
457+
# Tell the rfblaster to stop
458+
form.add_field("halt", "Halt execution")
431459
self.http_request(form)
432460
return True
433461

@@ -438,9 +466,11 @@ def transition_to_manual(self):
438466
def http_request(self, form=None):
439467
"""Make a HTTP request to the RFBlaster, optionally submitting a form"""
440468
if PY2:
441-
from urllib2 import urlopen, Request
469+
from urllib2 import urlopen, Request, URLError, httplib
470+
HTTPError = httplib.HTTPException
442471
else:
443472
from urllib.request import urlopen, Request
473+
from urllib.error import URLError, HTTPError
444474

445475
req = Request(self.address)
446476
if form is not None:
@@ -449,17 +479,38 @@ def http_request(self, form=None):
449479
req.add_header(b'Content-length', len(body))
450480
req.data = body
451481

452-
page = b''.join(urlopen(req, timeout=self.timeout).readlines())
453-
return page
482+
self._connection_attempt = 1
483+
self._kloned_attempted = False
484+
response = None
485+
while not response:
486+
try:
487+
self.netlogger.info('Connection attempt %i.' % self._connection_attempt)
488+
response = b''.join(urlopen(req, timeout=self.timeout).readlines())
489+
self.netlogger.info('Connected!')
490+
break
491+
except (URLError, HTTPError) as e:
492+
self.netlogger.warning(str(e))
493+
if self._connection_attempt < self.retries:
494+
self.netlogger.info('Connection failed. Trying again (%i more attempts remain).' % (self.retries - self._connection_attempt))
495+
self._connection_attempt += 1
496+
elif not self._kloned_attempted:
497+
self._kloned_attempted = True
498+
self.restart_kloned()
499+
self._connection_attempt = 1
500+
else:
501+
self.netlogger.error(str(e))
502+
raise e
503+
504+
return response
454505

455506
def get_web_values(self, page):
456507
page = page.decode('utf8')
457508
import re
458-
#prepare regular expressions for finding the values:
509+
# Prepare regular expressions for finding the values:
459510
search = re.compile(r'name="([fap])_ch(\d+?)_in"\s*?value="([0-9.]+?)"')
460-
webvalues = re.findall(search,page)
511+
webvalues = re.findall(search, page)
461512

462-
register_name_map = {'f':'freq','a':'amp','p':'phase'}
513+
register_name_map = {'f': 'freq', 'a': 'amp', 'p': 'phase'}
463514
newvals = {}
464515
for i in range(self.num_DDS):
465516
newvals['dds %d'%i] = {}
@@ -480,7 +531,7 @@ def get_web_values(self, page):
480531
return newvals
481532

482533
def check_remote_values(self):
483-
#read the webserver page to see what values it puts in the form
534+
# Read the webserver page to see what values it puts in the form
484535
return self.get_web_values(self.http_request())
485536

486537
def shutdown(self):

0 commit comments

Comments
 (0)