def __init__(self, ipv4, vessel_max=4, max_mfc_rate=0.5, setup_timeout=None): self.logger = BuiltinLogger("MechKLATest") self._max_mfc_rate = max_mfc_rate self._vessel_max = vessel_max self._setup_timeout = setup_timeout or (2 ** 31 - 1) self.app = open_hello(ipv4) self._reports = None
def __init__(self, ipv4, reactor_ctx=None, test_ctx=None): self.app = open_hello(ipv4) self.tests_to_run = [] self.tests_run = [] self.tests_skipped = [] self.tests_pending = [] self.ntests_run = 0 self.logger = BuiltinLogger(self.__class__.__name__ + datetime.now().strftime("%m-%d-%Y %H-%M")) self.logger.handlers.pop(0) # XXX bad practice if reactor_ctx is None: reactor_ctx = _default_r_ctx if test_ctx is None: test_ctx = _default_t_ctx self.reactor_ctx = reactor_ctx self.test_ctx = test_ctx
class MechKLATest: """ KLA test runner designed ground-up to work for *3L Mag Wheel* only! If using any other setup, review code to verify it will work correctly!!! Since mag drive uses headspace to "sparge" the vessel with oxygen, no operator activity with tubing, gases, etc is necessary in certain circumstances. This class ASSUMES THE FOLLOWING: * 1.3 hello software * Logger settings are correct * N2 gas is connected to a tank with sufficient volume, to N2 AND O2 inlets * Air is connected to a compressor (or tank w/ sufficient volume) to air inlet * Vessel is inserted in reactor, with Main and Micro gas lines connected * All ports on the top of the vessel are closed * Filter oven is only open line for gas to escape * Vessel has Main Gas connector on L-plate snipped. * Vessel has Micro Gas connector on L-plate intact! """ def __init__(self, ipv4, vessel_max=4, max_mfc_rate=0.5, setup_timeout=None): self.logger = BuiltinLogger("MechKLATest") self._max_mfc_rate = max_mfc_rate self._vessel_max = vessel_max self._setup_timeout = setup_timeout or (2 ** 31 - 1) self.app = open_hello(ipv4) self._reports = None def setup(self): self.logger.info("Initializing KLA Setup") app = self.app app.login() app.setag(1, 50) app.setdo(1, 0, 500) start = _time() self.logger.info("Begin setup. Waiting for DO < 20%") log_time = _time() + 10 while app.getdopv() > 20: t = _time() if t > log_time: log_time = int(t) + 10 self.logger.info("Waiting for DO to fall below 20.") self.logger.info("%d seconds: %.1f%% DO" % (t - start, app.getdopv())) _sleep(1) app.setdo(2, 0, 0) app.setmg(2, 0) def clear_headspace(self, media_volume): self.logger.info("Preparing to purge headspace") # math headspace = self._vessel_max - media_volume sleep_time = headspace / self._max_mfc_rate * 60 app = self.app app.login() app.setdo(2, 0) app.setmg(1, self._max_mfc_rate) self.logger.info("Purging headspace at %.3f LPM for %d seconds" % (self._max_mfc_rate, sleep_time)) time = _time sleep = _sleep t = time() end = t + sleep_time log_time = int(t) + 10 while t < end: if t > log_time: log_time = int(t) + 10 left = int(end - t) self.logger.info("Purging headspace. %s seconds remaining" % left) t = time() sleep(5) # login again in case sleep_time was long app.login() app.setmg(2, 0) def run(self, volume, experiments): # batches is list of batch names batches = self.run_experiments(volume, experiments) if self._reports is None: reports = self._reports = [] else: reports = self._reports batch_list = self.app.getBatches() for name in batches: id = batch_list.getbatchid(name) r = self.app.getdatareport_bybatchid(id) b = KLARawDataFile(name, None, r) reports.append(b) return reports def run_experiments(self, volume, experiments): """ @param volume: volume of media in L @param experiments: 3 tuple (ag_mode, ag_sp, flow_rate) @type experiments: ((int | float, int | float, int | float)) | list[(int | float, int | float, int | float)] @return: list of batches @rtype: list[str] """ batches = [] self.logger.info("Running %d experiments." % len(experiments)) for i, (mode, sp, flowrate) in enumerate(experiments, 1): self.logger.info("Running test %d of %d" % (i, len(experiments))) try: self.setup() except KeyboardInterrupt: self.logger.error("Got keyboard interrupt, skipping setup.") try: self.clear_headspace(volume) except KeyboardInterrupt: self.logger.error("Got keyboard interrupt, skipping headspace purge.") try: b = self.experiment(mode, sp, flowrate, volume) batches.append(b) except KeyboardInterrupt: self.logger.error("Got keyboard interrupt, skipping test.") continue finally: mv = self.app.gpmv() if mv["do"]["mode"] != 2: while True: try: self.app.login() self.app.setdo(2, 0, 0) break except Exception: self.logger.error("Error shutting down test.") self.app.reconnect() return batches def experiment(self, ag_mode, ag_sp, flow_rate, volume): """ @param flow_rate: flow rate in *mL per min* """ app = self.app app.login() time = _time self.logger.info("Initializing Agitation with mode=%s sp=%s." % (ag_mode, ag_sp)) app.setag(ag_mode, ag_sp) # if setpoint is auto mode, wait for pv to reach correct value if ag_mode == 0: timeout = time() + 10 * 60 log_time = time() + 10 while True: pv = app.getagpv() if ag_sp - 1 < pv < ag_sp + 1: break t = time() if t > log_time: log_time = int(t) + 10 self.logger.info("Waiting for Agitation to reach setpoint. PV = %d." % app.getagpv()) if t > timeout: raise KLAError("Agitation didn't reach setpoint.") _sleep(1) app.setmg(1, flow_rate / 1000) self.logger.info("Beginning KLA Experiment.") batch_name = "kla%s-%s-%s-%s" % (ag_mode, volume, ag_sp, flow_rate) self.logger.info("Starting new batch named '%s'." % batch_name) if app.batchrunning(): app.endbatch() app.startbatch(batch_name) start = time() end = start + 14 * 60 log_time = start + 10 while True: t = time() pv = app.getdopv() if t > log_time: self.logger.info("Test running, %d seconds passed. DO PV = %.1f." % (t - start, pv)) log_time += 10 if t > end: break if pv > 90: break self.logger.info("Test finished. DO PV = %.1f after %d seconds." % (app.getdopv(), time() - start)) self.logger.info("Ending batch") app.endbatch() return batch_name
class AirKLATestRunner: """ Run a group of KLA tests on an air wheel reactor. """ def __init__(self, ipv4, reactor_ctx=None, test_ctx=None): self.app = open_hello(ipv4) self.tests_to_run = [] self.tests_run = [] self.tests_skipped = [] self.tests_pending = [] self.ntests_run = 0 self.logger = BuiltinLogger(self.__class__.__name__ + datetime.now().strftime("%m-%d-%Y %H-%M")) self.logger.handlers.pop(0) # XXX bad practice if reactor_ctx is None: reactor_ctx = _default_r_ctx if test_ctx is None: test_ctx = _default_t_ctx self.reactor_ctx = reactor_ctx self.test_ctx = test_ctx def print(self, *args, **kwargs): print(*args, **kwargs) msg = kwargs.get("sep", " ").join(args).replace("\r", "") if msg: self.logger.info(msg) def import_batch(self, batchname): filename = self.test_ctx.generate_filename(batchname) t = AirKLATest(self.app, 0, 0, 0, batchname, self.reactor_ctx, self.test_ctx) r = KLARawDataFile.from_download(batchname, filename, self.app.ipv4) t.report = r self.tests_run.append(t) def pickle_completed(self): pkl_file = self.test_ctx.savedir + "klapickle\\airklatestpikle.pkl" safe_pickle(self.tests_run, pkl_file) def add_test(self, test): """ @type test: AirKLATest """ test.print = self.print self.tests_to_run.append(test) def create_test(self, main_sp, micro_sp, volume, name): t = AirKLATest(self.app, main_sp, micro_sp, volume, name, self.reactor_ctx, self.test_ctx) self.add_test(t) return t def skip_current_test(self): t = self.tests_pending.pop() self.tests_skipped.append(t) self.app.login() if self.reactor_ctx.is_mag: self.app.setmg(2, 0) else: self.app.setag(2, 0) self.app.setph(2, 0, 0) self.app.setdo(2, 0, 0) self.app.logout() def run_once(self): self.ntests_run += 1 t = self.tests_to_run.pop() self.print("-------------------------------------------") self.print("Test #%d starting: %s" % (self.ntests_run, t.get_info())) self.tests_pending.append(t) try: t.run() except SkipTest as e: self.print(e.args) self.skip_current_test() except Exception: traceback.print_exc() self.skip_current_test() else: self.tests_pending.pop() self.tests_run.append(t) assert t.report def run_all(self): # run all tests. reverse list in the beginning, # then run 1 by 1 using .pop() to iterate in # correct order while removing from list. self.tests_to_run.reverse() while self.tests_to_run: self.run_once() if self.tests_skipped: self.print("------------------------") self.print("Skipped tests:") for t in self.tests_skipped: self.print(t.get_info())