예제 #1
0
def main(args, stdin, stdout, stderr):
    "Wait for a minimum time or linearity, then run P gradient, writing real-time data."
    
    ## run the experiment
    
    # open output file, do not overwrite!
    with open(args['file_log'], 'x') as hand_log:
    
        # compose and write header
        list_head = ["clock", "watch", "state"] + list(states.head()) + ["T_int", "T_ext", "T_act", "P_act", "intensity"]
        line_head = "\t".join(list_head)
        hand_log.write(line_head + '\n')
        hand_log.flush()
        
        # now we're opening serial connections, which need to be closed cleanly on exit
        try:
            # init instruments
            print("connecting...", file=stderr)
            bath = isotemp6200.IsotempController(port=args['port_bath'])
            print("temperature controller √", file=stderr)
            pump = isco260D.ISCOController(port=args['port_pump'])
            print("pressure controller    √", file=stderr)
            spec = rf5301.RF5301(port=args['port_spec'])
            print("fluorospectrometer     √", file=stderr)
            
            ## hardware init
            print("initializing...", file=stderr)
            # start bath circulator
            print("bath", end=' ', file=stderr)
            # set precision
            prec_bath = 2 # number of decimal places
            while not bath.temp_prec(prec_bath):
                pass
            while not bath.on():
                bath.on(True)
            print('√', file=stderr)
                
            # clear pump and write gradient program
            print("pump", end=' ', file=stderr)
            while not pump.remote():
                pass
            while not pump.clear():
                pass
			# write gradient
			# for now, gradient should be programmed at pump controller
            # get initial volume
            vol_start = False
            while not vol_start:
                vol_start = pump.vol_get()
            print("√ V0 = {} mL".format(vol_start), file=stderr)
                
            # open the shutter, unless in auto
            if not args["auto_shut"]:
                while not spec.shutter(True):
                    pass
            
            # declare async queues
            queue_bath = deque(maxlen=1)
            queue_pump = deque(maxlen=1)
            queue_spec = deque(maxlen=1)
            
            # start polling threads
            # all device instances have RLocks!
            bath_free = threading.Event()
            pump_free = threading.Event()
            spec_free = threading.Event()
            [event.set() for event in (bath_free, pump_free, spec_free)]
            threading.Thread(name="pollbath", target=poll, args=(bath, bath_free, queue_bath)).start()
            threading.Thread(name="pollpump", target=poll, args=(pump, pump_free, queue_pump)).start()
            threading.Thread(name="pollspec", target=poll, args=(spec, spec_free, queue_spec)).start()
            
            ## run experiment
            
            # start experiment timer (i.e. stopwatch)
            time_start = time.time()
            
            time_air_tot = 0
            save_air = True
        
            list_data = [0]*10
            
            # iterate over test states
            for state_num in range(states.shape[0]):
            
                # make dicts for this state, the last, and the next
                # takes about 1 ms
                state_curr = states.iloc[state_num+0].to_dict()
                if state_num: 
                    state_prev = states.iloc[state_num-1].to_dict()
                else:
                    # if first state
                    state_prev = {key: 0 for key in state_curr.keys()}
                    chg_prev = {key: True for key in state_curr.keys()}
                if state_num < states.shape[0]-1:
                    state_next = states.iloc[state_num+1].to_dict()
                else:
                    # if final state
                    state_next = {key: 0 for key in state_curr.keys()}
                    chg_next = {key: True for key in state_curr.keys()}
                
                # which params have changed since previous state?
                chg_prev = {key: (state_curr[key] != state_prev[key]) for key in state_curr.keys()}
                chg_next = {key: (state_curr[key] != state_next[key]) for key in state_curr.keys()}
                
                time_state = time.time() # mark time
                waited = False # did the state have to wait for stability?
                readings = 0 # reset n counter
                
                # status update
                print("state {}/{}:".format(state_num+1, states.shape[0]), file=stderr)
                print(state_curr, file=stderr)
                
                # set temp persistently
                # this is the actual setpoint passed to the waterbath
                temp_set = round(temp_ext2int(temp_act2ext(state_curr['T_set'])), prec_bath)
                temp_tol = round(args['tol_T'] / 1.18052628 * 1.312841332, prec_bath)
                bath_free.clear()
                while not isclose(bath.temp_set(), temp_set):
                    print("setting temperature to {}˚C".format(state_curr['T_set']), file=stderr, end=' ')
                    if bath.temp_set(temp_set): print('√', file=stderr)
                bath_free.set()
                
                # set pres persistently
                pump_free.clear()
                while not isclose(pump.press_set(), state_curr['P_set']):
                    print("setting pressure to {} bar".format(state_curr['P_set']), file=stderr, end=' ')
                    if pump.press_set(state_curr['P_set']): print('√', file=stderr)
                pump_free.set()
                    
                ## set the excitation wavelength
                #while not isclose(spec.ex_wl(), state_curr['wl_ex']):
                #    print("setting excitation WL to {} nm".format(state_curr['wl_ex']), file=stderr, end=' ')
                #    if spec.ex_wl(state_curr['wl_ex']): print('√', file=stderr)
                #    
                ## set the excitation wavelength
                #while not isclose(spec.em_wl(), state_curr['wl_em']):
                #    print("setting emission WL to {} nm".format(state_curr['wl_em']), file=stderr, end=' ')
                #    if spec.ex_wl(state_curr['wl_em']): print('√', file=stderr)
                
                # temporary WL setters
                # Persistence implemented over cycles to improve efficiency;
                # note that this checks the previous data row.
                #NTS 20200719 reimplement list_data as a dict!!!
                if not ((state_curr['wl_ex'] == list_data[4]) and (state_curr['wl_em'] == list_data[5])):
                    spec_free.clear()
                    if (state_curr['wl_ex'] == 340) and (state_curr['wl_em'] == 440):
                        print("setting wavelengths to Laurdan blue", file=stderr, end=' ')
                        if spec.wl_set_laurdan_blu(): print('√', file=stderr)
                    elif (state_curr['wl_ex'] == 340) and (state_curr['wl_em'] == 490):
                        print("setting wavelengths to Laurdan red", file=stderr, end=' ')
                        if spec.wl_set_laurdan_red(): print('√', file=stderr)
                    spec_free.set()
                
                # init a log table for the state
                trails = pd.DataFrame(columns = list_head)
                
                # data logging loop
                data_dict = {}
                # init the data dict. Persistent the first time.
                for dq in (queue_bath, queue_pump, queue_spec):
                    while True:
                        # ensure that *something* gets popped out so the dict is complete
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass
                                
                while True:
                
                    time_cycle = time.time()
                    
                    # DATA FIRST
                    for dq in (queue_bath, queue_pump, queue_spec):
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass
                    # add internal data
                    data_dict.update(
                        {
                            "clock" : time.strftime("%Y%m%d %H%M%S"),
                            "watch" : time.time() - time_start,
                            "state" : state_num,
                            "T_set" : state_curr["T_set"],
                            "P_set" : state_curr["P_set"],
                            #"wl_ex" : state_curr["wl_ex"],
                            #"wl_em" : state_curr["wl_em"]
                        }
                    )
                    # round off the modeled T_act
                    data_dict["T_act"] = round(data_dict["T_act"], prec_bath)
                    
                    # SAFETY SECOND
                    # check for pressure system leak
                    if (data_dict["vol"] - vol_start) > args["vol_diff"]:
                        pump_free.clear()
                        pump.clear()
                        raise Exception("Pump has discharged > {} mL!".format(args["vol_diff"]))
                        
                    # AIR SAVER
                    # if transition's been running for >2x the stability time,
                    # but bath internal temperature still not in range,
                    # shut air off temporarily
                    if args["air_saver"] and \
                        ((time_cycle - time_state) >= 2 * args["eq_min"]) and \
                        (data_dict["T_int"] < temp_set - temp_tol) or \
                        (data_dict["T_int"] > temp_set + temp_tol):
                        if waited and not save_air: print(file=stderr)
                        if not save_air: print("air saver activated", end='', file=stderr)
                        save_air = True
                    else:
                        save_air = False
                        
                    # control the air system
                    #NTS 20200720: pin set executes twice, which is annoying but not fatal
                    if data_dict['T_act'] <= args["dewpoint"] and not save_air:
                        # if it's cold
                        if not data_dict['air']:
                            # and air is off
                            pump_free.clear()
                            if waited: print(file=stderr)
                            print("turning air ON", file=stderr, end=' ')
                            pump.digital(0, 1)
                            print("√", file=stderr)
                            pump_free.set()
                            data_dict['air'] = True
                            # start a timer for air being on
                            time_air = time.time()
                            print("total air time {} s".format(round(time_air_tot)), file=stderr)
                    else:
                        # if it's warm
                        if data_dict['air']:
                            # and air is on
                            pump_free.clear()
                            if waited: print(file=stderr)
                            print("\nturning air OFF", file=stderr, end=' ')
                            pump.digital(0, 0)
                            print("√", file=stderr)
                            pump_free.set()
                            data_dict['air'] = False
                            # add air time to the total
                            time_air_tot += (time.time() - time_air)
                            print("total air time {} s".format(round(time_air_tot)), file=stderr)
                    
                    # does the state change require equilibration?
                    if any([chg_prev[var] for var in args["vars_eq"]]):
                        # if any of the slow params have changed from last state
                        need2wait = True
                    else:
                        need2wait = False
                    
                    # put data in the temporary trailing DF
                    trails = trails.append(data_dict, ignore_index=True)
                    # cut the DF down to within the trailing time
                    trails = trails[trails['watch'] >= trails['watch'].iloc[-1] - args["eq_min"]]
                    
                    # if the fluor reading has changed
                    # this check takes about 0.1 ms
                    if (trails.shape[0] == 1) or (trails["intensity"].iloc[-1] !=  trails["intensity"].iloc[-2]):
                        # write data to file
                        hand_log.write('\t'.join([str(data_dict[col]) for col in list_head])+'\n')
                        hand_log.flush()
                        
                    #NTS 20200719: It would be nice to abstract pressure and temp stability!
                    try:
                        # note that this checks range of the internal temperature, and stability of the actual temperature
                        temp_in_range = ((max(trails["T_int"]) <= temp_set + temp_tol) and (min(trails["T_int"]) >= temp_set - temp_tol) and ((max(trails["T_act"]) - min(trails["T_act"]) <= 2 * args["tol_T"])))
                        pres_in_range = ((max(trails["P_act"]) <= state_curr["P_set"] + args["tol_P"]) and (min(trails["P_act"]) >= state_curr["P_set"] - args["tol_P"]))
                    except:
                        # in case the dataframe is Empty
                        temp_in_range = False
                        pres_in_range = False
                        pass
                    
                    # if we're equilibrated
                    # and in range
                    # or state has timed out
                    # and windows are defogged
                    if ((not need2wait or (time_cycle - time_state) >= args["eq_min"] and \
                       temp_in_range and pres_in_range and not save_air) or \
                       (time_cycle - time_state) >= args["eq_max"]) and \
                       (data_dict['T_act'] > args["dewpoint"] or (time_cycle - time_air) >= args["air_time"]) :
                       
                        if waited: print(file=stderr) # newline
                        waited = False
                        
                        # open the shutter
                        if (not readings) and args["auto_shut"] and any([chg_prev[var] for var in args["vars_eq"]]): 
                            spec_free.clear()
                            while not spec.shutter(True):
                                pass
                            spec_free.set()
                        # take some readings
                        if (trails.shape[0] == 1) or (trails["intensity"].iloc[-1] !=  trails["intensity"].iloc[-2]):
                            if readings: print("reading {}: {} AU\r".format(readings, trails['intensity'].iloc[-1]), end='', file=stderr)
                            readings += 1
                        # break out of loop to next state
                        if (readings > args["n_read"]):
                            if args["auto_shut"] and any([chg_next[var] for var in args["vars_eq"]]):
                                spec_free.clear()
                                while not spec.shutter(False):
                                    pass
                                spec_free.set()
                            print(file=stderr)
                            break
                            
                    else:
                        # what are we waiting for?
                        print("waiting {} s to get {} s of stability\r".format(round(time.time()-time_state), args["eq_min"]), end='', file=stderr)
                        waited = True
                        
                    # prescribed sleep
                    try:
                        time.sleep((args["cycle_time"] / 1000) - (time.time() - (time_cycle)))
                    except:
                        pass
                    
            # shut down when done
            pump_free.clear()
            pump.digital(0,0)
            pump.pause()
            pump.disconnect()
            bath_free.clear()
            bath.on(False)
            bath.disconnect()
            spec_free.clear()
            spec.shutter(False)
            spec.disconnect()
            sys.exit(0)
    
        except:
            pump_free.clear()
            pump.pause()
            pump.digital(0,0) # turn the air off!
            spec_free.clear()
            spec.ack()
            spec.shutter(False)
            traceback.print_exc()
예제 #2
0
def main(args, stdin, stdout, stderr):
    "Run a temperature/parameter scan and store output to a file."

    # if args are passed as namespace, convert it to dict
    try:
        args = vars(args)
    except:
        pass

    if not stdin.isatty():
        # if a state table is passed on stdin, read it
        print("reading states from stdin", file=stderr, flush=True)
        states = pd.read_csv(stdin, sep='\t')
    else:
        print("ERR: you need to pass the state table on stdin!",
              file=stderr,
              flush=True)
        exit(1)

    ## run the experiment

    # open output file, do not overwrite!
    #with open(args['file_log'], 'x') as hand_log:
    with open(args['file_log'], 'w') as hand_log:

        ## variables tracking the expt schedule
        #vars_sched = ["clock", "watch", "state"]
        ## externally measured and derived variables
        #vars_measd = ["T_int", "T_ext", "T_act", "P", "I", "D", "P_act", "vol", "intensity", "T_amb", "H_amb", "dewpt", "air"]
        ## compose and write header - states.head() are the setpoint variables
        #list_head = vars_sched + list(states.head()) + vars_measd
        #line_head = "\t".join(list_head)
        #hand_log.write(line_head + '\n')
        #hand_log.flush()

        # now we're opening serial connections, which need to be closed cleanly on exit
        try:
            # init instruments
            print("connecting...", file=stderr, flush=True)
            print("fluorospectrometer     ", end='', file=stderr, flush=True)
            spec = rf5301.RF5301(port=args['port_spec'])
            print("√", file=stderr, flush=True)
            print("aux microcontroller    ", end='', file=stderr, flush=True)
            amcu = auxmcu.AuxMCU(port=args['port_amcu'])
            print("√", file=stderr, flush=True)
            print("temperature controller ", end='', file=stderr, flush=True)
            bath = neslabrte.NeslabController(port=args['port_bath'])
            print("√", file=stderr, flush=True)
            print("pressure controller    ", end='', file=stderr, flush=True)
            pump = isco260D.ISCOController(port=args['port_pump'])
            print("√", file=stderr, flush=True)

            ## hardware init
            print("initializing...", file=stderr, flush=True)

            # set spec slits and gain
            print("spec        ", end='', file=stderr, flush=True)
            # autozero with emission slit closed
            while not spec.slit_em(0):
                pass
            while not spec.zero():
                pass
            # open the shutter, unless in auto
            if not args["auto_shut"]:
                while not spec.shutter(True):
                    pass
                print('.', end='', file=stderr, flush=True)
            print(' √', file=stderr, flush=True)

            # initialize the AMCU
            print("auxiliary    ", end='', file=stderr, flush=True)
            amcu.lamp(True)
            print(' √', file=stderr, flush=True)

            # start bath circulator
            print("bath        ", end='', file=stderr, flush=True)
            # enter topside cal coefficients
            bath.cal_int.reset(*args["rtd_cal"])
            # start the bath
            #while not bath.on():
            #    bath.on(True)
            bath.on(True)  # 20220105 hack
            print(' √', file=stderr, flush=True)

            # clear and start pump
            print("pump", end='', file=stderr, flush=True)
            while not pump.remote():
                pass
            print('.', end='', file=stderr, flush=True)
            while not pump.clear():
                pass
            print('.', end='', file=stderr, flush=True)
            while not pump.run():
                pass
            print('.', end='', file=stderr, flush=True)
            # get initial volume
            vol_start = None
            while not vol_start:
                vol_start = pump.vol_get()
            print("         √ V0 = {} mL".format(vol_start),
                  file=stderr,
                  flush=True)

            # declare async queues
            queue_bath = deque(maxlen=1)
            queue_pump = deque(maxlen=1)
            queue_spec = deque(maxlen=1)
            queue_amcu = deque(maxlen=1)

            # start polling threads
            # all device instances have RLocks!
            bath_free = threading.Event()
            pump_free = threading.Event()
            spec_free = threading.Event()
            amcu_free = threading.Event()
            [
                event.set()
                for event in (bath_free, pump_free, spec_free, amcu_free)
            ]
            threading.Thread(name="pollbath",
                             target=poll,
                             args=(bath, bath_free, queue_bath)).start()
            threading.Thread(name="pollpump",
                             target=poll,
                             args=(pump, pump_free, queue_pump)).start()
            threading.Thread(name="pollspec",
                             target=poll,
                             args=(spec, spec_free, queue_spec)).start()
            threading.Thread(name="pollamcu",
                             target=poll,
                             args=(amcu, amcu_free, queue_amcu)).start()

            ## run experiment

            # start experiment timer (i.e. stopwatch)
            time_start = time.time()

            print("waiting for data streams...", file=stderr, flush=True)
            # init the data dict. Persistent at start of program.
            data_dict = {
                "clock": time.strftime("%Y%m%d %H%M%S"),
                "watch": time.time() - time_start,
            }
            for dq in (queue_bath, queue_pump, queue_spec, queue_amcu):
                while True:
                    #print(data_dict) #TEST
                    # ensure that *something* gets popped out so the dict is complete
                    try:
                        data_dict.update(dq.popleft())
                        break
                    except:
                        pass

            # iterate over test states
            for state_num in range(states.shape[0]):

                # make dict for this state
                state_curr = states.iloc[state_num + 0].to_dict()
                try:
                    state_next = states.iloc[state_num + 1].to_dict()
                except:
                    pass

                # status update
                print("state {}/{}:".format(state_num + 1, states.shape[0]),
                      file=stderr,
                      flush=True)
                print(state_curr, file=stderr, flush=True)

                # before entering the first state, write the data file header
                if not state_num:
                    hand_log.write('\t'.join(
                        sorted(
                            set(
                                list(state_curr.keys()) +
                                list(data_dict.keys())))) + '\n')

                ## SETTING COMMANDS
                ## once these have executed, state_curr and data_dict can be merged
                ## without loss of information

                # set temp
                bath_free.clear()
                time.sleep(1)
                while not isclose(
                        bath.temp_set(),
                        round(bath.cal_int.act2ref(state_curr['T_set']), 2)):
                    print("setting temperature to {}°C".format(
                        state_curr['T_set']),
                          file=stderr,
                          flush=True,
                          end=' ')
                    bath.temp_set(bath.cal_int.act2ref(state_curr['T_set']))
                    print('√', file=stderr, flush=True)
                bath_free.set()

                # set pressure persistently
                pump_free.clear()
                while not isclose(pump.press_set(), state_curr['P_set']):
                    print("setting pressure to {} bar".format(
                        state_curr['P_set']),
                          file=stderr,
                          flush=True,
                          end=' ')
                    if pump.press_set(state_curr['P_set']):
                        print('√', file=stderr, flush=True)
                pump_free.set()

                # mark time for start of state
                # doing this before wavelengths saves a few seconds
                time_state = time.time()
                waited = False
                # init n counter
                readings = 0

                # set wavelengths

                ## set the excitation wavelength
                #while not isclose(spec.wl_ex(), state_curr['wl_ex']):
                #    print("setting excitation WL to {} nm".format(state_curr['wl_ex']), file=stderr, flush=True, end=' ')
                #    if spec.wl_ex(state_curr['wl_ex']): print('√', file=stderr, flush=True)
                #
                ## set the emission wavelength
                #while not isclose(spec.wl_em(), state_curr['wl_em']):
                #    print("setting emission WL to {} nm".format(state_curr['wl_em']), file=stderr, flush=True, end=' ')
                #    if spec.wl_ex(state_curr['wl_em']): print('√', file=stderr, flush=True)

                # temporary WL setters
                # Persistence implemented over cycles to improve efficiency;
                # note that this checks the previous data row.
                if not (
                        # do any requests need to be sent to the spec?
                        state_curr['wl_ex'] == data_dict['wl_ex']
                        and state_curr['wl_em'] == data_dict['wl_em']
                        and state_curr['slit_ex'] == data_dict['slit_ex']
                        and state_curr['slit_em'] == data_dict['slit_em']):
                    #spec_free.clear() # seems like maybe these flags should be removed bc they slow things down?
                    if (state_curr['wl_ex'] == 350) and (state_curr['wl_em']
                                                         == 428):
                        print("setting wavelengths for DPH",
                              file=stderr,
                              flush=True,
                              end=' ')
                        if spec.wl_set_dph():
                            print('√', file=stderr, flush=True)
                    elif (state_curr['wl_ex'] == 340) and (state_curr['wl_em']
                                                           == 440):
                        print("setting wavelengths to 340/440",
                              file=stderr,
                              flush=True,
                              end=' ')
                        if spec.wl_set_340_440():
                            print('√', file=stderr, flush=True)
                    elif (state_curr['wl_ex'] == 340) and (state_curr['wl_em']
                                                           == 490):
                        print("setting wavelengths to 340/490",
                              file=stderr,
                              flush=True,
                              end=' ')
                        if spec.wl_set_340_490():
                            print('√', file=stderr, flush=True)
                    elif (state_curr['wl_ex'] == 410) and (state_curr['wl_em']
                                                           == 440):
                        print("setting wavelengths to 410/440",
                              file=stderr,
                              flush=True,
                              end=' ')
                        if spec.wl_set_410_440():
                            print('√', file=stderr, flush=True)
                    elif (state_curr['wl_ex'] == 410) and (state_curr['wl_em']
                                                           == 490):
                        print("setting wavelengths to 410/490",
                              file=stderr,
                              flush=True,
                              end=' ')
                        if spec.wl_set_410_490():
                            print('√', file=stderr, flush=True)

                    # slits
                    if state_curr['slit_ex'] != data_dict['slit_ex']:
                        print("setting ex slit to {} nm".format(
                            state_curr['slit_ex']),
                              file=stderr,
                              flush=True,
                              end=' ')
                        if spec.slit_ex(state_curr['slit_ex']):
                            print('√', file=stderr, flush=True)
                    if state_curr['slit_em'] != data_dict['slit_em']:
                        print("setting em slit to {} nm".format(
                            state_curr['slit_em']),
                              file=stderr,
                              flush=True,
                              end=' ')
                        if spec.slit_em(state_curr['slit_em']):
                            print('√', file=stderr, flush=True)
                    #spec_free.set()

                # add input data to output buffer
                data_dict.update(state_curr)

                # init buffer for previous data line
                data_prev = {key: None for key in data_dict.keys()}

                # data logging loop
                while True:

                    time_cycle = time.time()

                    # DATA FIRST
                    for dq in (queue_bath, queue_pump, queue_spec, queue_amcu):
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass
                    # add timing data
                    data_dict.update({
                        "clock": time.strftime("%Y%m%d %H%M%S"),
                        "watch": time.time() - time_start,
                    })

                    ## Cyclewise instrument control
                    ## for stuff that needs to be monitored in real time

                    # SAFETY SECOND
                    # check for pressure system leak
                    if (data_dict["vol"] - vol_start) > args["vol_diff"]:
                        pump_free.clear()
                        pump.clear()
                        raise Exception("Pump has discharged > {} mL!".format(
                            args["vol_diff"]))

                    # control the air system
                    # if it's cold and air is off
                    if (data_dict['T_act'] <= data_dict["dewpt"] +
                            args["dew_tol"]) and not data_dict['air']:
                        pump_free.clear()
                        if waited: print(file=stderr, flush=True)
                        print("turning air ON",
                              file=stderr,
                              flush=True,
                              end=' ')
                        while not pump.digital(0, 1):
                            pass
                        print("√", file=stderr, flush=True, end='\r')
                        pump_free.set()
                        data_dict['air'] = True
                    # if it's warm and the air is on
                    elif (data_dict['T_act'] >
                          (data_dict["dewpt"] +
                           args["dew_tol"])) and data_dict['air']:
                        # and air is on
                        pump_free.clear()
                        if waited: print(file=stderr, flush=True)
                        print("turning air OFF",
                              file=stderr,
                              flush=True,
                              end=' ')
                        while not pump.digital(0, 0):
                            pass
                        print("√", file=stderr, flush=True, end='\r')
                        pump_free.set()
                        data_dict['air'] = False

                    #print("P "+str(data_prev["intensity"]))
                    #print("C "+str(data_dict["intensity"]))

                    # if state wait time has not elapsed
                    waiting = (time.time() - time_state) < data_dict["time"]
                    if waiting:
                        print("waiting {}/{} s    ".format(
                            round(time.time() - time_state),
                            data_dict["time"]),
                              file=sys.stderr,
                              end='\r',
                              flush=True)
                        waited = True
                    else:
                        if waited: print(file=stderr, flush=True)  # newline

                        # open the shutter
                        if (not readings) and args["auto_shut"] and waited:
                            spec_free.clear()  # good or bad?
                            while not spec.shutter(True):
                                pass
                            spec_free.set()
                            time_open = time.time()

                        # reset
                        waited = False

                    # fluor intensity is the fastest-changing variable
                    # so only save data when it changes
                    # this is the output block
                    if data_dict["intensity"] != data_prev["intensity"]:

                        # write data to file whether it counts as a reading or not
                        hand_log.write('\t'.join([
                            str(data_dict[key])
                            for key in sorted(data_dict.keys())
                        ]) + '\n')
                        hand_log.flush()
                        # and buffer the data
                        data_prev.update(data_dict)

                        if not waiting:
                            # increment the reading count
                            readings += 1
                            print("reading {}/{} s: {} AU  ".format(
                                readings, data_dict["n_read"],
                                data_dict["intensity"]),
                                  file=sys.stderr,
                                  end='\r',
                                  flush=True)
                            # once sufficient readings have been taken
                            if readings >= data_dict["n_read"]:
                                print(file=stderr, flush=True)  # newline
                                # if there is a wait between states, close shutter
                                if state_next["time"]:
                                    spec_free.clear()  # good or bad?
                                    while not spec.shutter(False):
                                        pass
                                    spec_free.set()
                                    time_shut = time.time()
                                # escape to next state
                                break

            # shut down when done
            pump_free.clear()
            pump.digital(0, 0)
            pump.pause()
            pump.disconnect()
            bath_free.clear()
            bath.on(False)
            bath.disconnect()
            spec_free.clear()
            spec.shutter(True)
            spec.disconnect()
            amcu_free.clear()
            amcu.__ser__.send_break(duration=amcu.__ser__.timeout + 0.1)
            time.sleep(amcu.__ser__.timeout + 0.1)
            amcu.lamp(False)
            sys.exit(0)

        except:
            # on failure:
            pump_free.clear()
            pump.pause()
            pump.digital(0, 0)  # turn the air off!
            spec_free.clear()
            spec.ack()
            spec.shutter(False)
            amcu_free.clear()
            amcu.__ser__.send_break(duration=amcu.__ser__.timeout + 0.1)
            time.sleep(amcu.__ser__.timeout + 0.1)
            amcu.lamp(False)
            traceback.print_exc()
예제 #3
0
def main(args, stdin, stdout, stderr):
    "Run a temperature/parameter scan and store output to a file."

    # if args are passed as namespace, convert it to dict
    try:
        args = vars(args)
    except:
        pass

    if not stdin.isatty():
        # if a state table is passed on stdin, read it
        print("reading states from stdin", file=stderr)
        states = pd.read_csv(stdin, sep='\t')
    else:
        print("ERR: you need to pass the state table on stdin!", file=stderr)
        exit(1)

    ## run the experiment

    # open output file, do not overwrite!
    with open(args['file_log'], 'x') as hand_log:

        # variables tracking the expt schedule
        vars_sched = ["clock", "watch", "state"]
        # externally measured and derived variables
        vars_measd = [
            "T_int", "T_ext", "T_act", "P", "I", "D", "P_act", "vol",
            "intensity", "T_amb", "H_amb", "dewpt", "air"
        ]
        # compose and write header - states.head() are the setpoint variables
        list_head = vars_sched + list(states.head()) + vars_measd
        line_head = "\t".join(list_head)
        hand_log.write(line_head + '\n')
        hand_log.flush()

        # now we're opening serial connections, which need to be closed cleanly on exit
        try:
            # init instruments
            print("connecting...", file=stderr)
            print("fluorospectrometer     √", file=stderr)
            amcu = auxmcu.AuxMCU(port=args['port_amcu'])
            print("aux microcontroller    √", file=stderr)
            bath = isotemp6200.IsotempController(port=args['port_bath'])
            print("temperature controller √", file=stderr)
            pump = isco260D.ISCOController(port=args['port_pump'])
            print("pressure controller    √", file=stderr)
            spec = rf5301.RF5301(port=args['port_spec'])

            ## hardware init
            print("initializing...", file=stderr)

            # set spec slits and gain
            print("spec", end='', file=stderr)
            # open the shutter, unless in auto
            if not args["auto_shut"]:
                while not spec.shutter(True):
                    pass
                print('.', end='', file=stderr)
            print(' √', file=stderr)

            # initialize the filter wheels
            print("auxiliary", file=stderr)
            amcu.lamp(True)
            #amcu.wheels_init()

            # start bath circulator
            print("bath", end='', file=stderr)

            # init topside PID
            pid = PID(1, 0, 85, setpoint=states.loc[states.index[0], "T_set"])
            # windup preventer
            pid.output_limits = (-20, 20)
            # enter topside cal coefficients
            bath.cal_ext.reset(*args["rtd_cal"])

            # set controller gains
            while not all(bath.pid('H', 0.8, 0, 0)):
                pass
            print('.', end='', file=stderr)
            while not all(bath.pid('C', 1, 0, 0)):
                pass
            print('.', end='', file=stderr)
            # set precision (number of decimal places)
            while not bath.temp_prec(2):
                pass
            print('.', end='', file=stderr)
            # set controller to listen to external RTD
            while not bath.probe_ext(True):
                pass
            print('.', end='', file=stderr)
            # finally, start the bath
            while not bath.on():
                bath.on(True)
            print(' √', file=stderr)

            # clear and start pump
            print("pump", end='', file=stderr)
            while not pump.remote():
                pass
            print('.', end='', file=stderr)
            while not pump.clear():
                pass
            print('.', end='', file=stderr)
            while not pump.run():
                pass
            print('.', end='', file=stderr)
            # get initial volume
            vol_start = None
            while not vol_start:
                vol_start = pump.vol_get()
            print(" √ V0 = {} mL".format(vol_start), file=stderr)

            # declare async queues
            queue_bath = deque(maxlen=1)
            queue_pump = deque(maxlen=1)
            queue_spec = deque(maxlen=1)
            queue_amcu = deque(maxlen=1)

            # start polling threads
            # all device instances have RLocks!
            bath_free = threading.Event()
            pump_free = threading.Event()
            spec_free = threading.Event()
            amcu_free = threading.Event()
            [
                event.set()
                for event in (bath_free, pump_free, spec_free, amcu_free)
            ]
            threading.Thread(name="pollbath",
                             target=poll,
                             args=(bath, bath_free, queue_bath, pid)).start()
            threading.Thread(name="pollpump",
                             target=poll,
                             args=(pump, pump_free, queue_pump)).start()
            threading.Thread(name="pollspec",
                             target=poll,
                             args=(spec, spec_free, queue_spec)).start()
            threading.Thread(name="pollamcu",
                             target=poll,
                             args=(amcu, amcu_free, queue_amcu)).start()

            ## run experiment

            # dict links setp vars to meas vars
            #NTS 20210113 there's gotta be a better place for these mappings
            setp2meas = {"T_set": "T_act", "P_set": "P_act"}

            # start experiment timer (i.e. stopwatch)
            time_start = time.time()

            # iterate over test states
            for state_num in range(states.shape[0]):

                # make dicts for this state, the last, and the next
                # takes about 1 ms
                state_curr = states.iloc[state_num + 0].to_dict()
                if state_num:
                    state_prev = states.iloc[state_num - 1].to_dict()
                else:
                    # if first state
                    state_prev = {key: 0 for key in state_curr.keys()}
                    chg_prev = {key: True for key in state_curr.keys()}
                if state_num < states.shape[0] - 1:
                    state_next = states.iloc[state_num + 1].to_dict()
                else:
                    # if final state
                    state_next = {key: 0 for key in state_curr.keys()}
                    chg_next = {key: True for key in state_curr.keys()}

                # which params have changed since previous state?
                chg_prev = {
                    key: (state_curr[key] != state_prev[key])
                    for key in state_curr.keys()
                }
                chg_next = {
                    key: (state_curr[key] != state_next[key])
                    for key in state_curr.keys()
                }

                time_state = time.time()  # mark time when state starts
                waited = False  # did the state have to wait for stability?
                sat = False  # did the dye have to relax?
                readings = 0  # reset n counter

                # status update
                print("state {}/{}:".format(state_num + 1, states.shape[0]),
                      file=stderr)
                print(state_curr, file=stderr)

                # data logging loop
                data_dict = {}
                # init the data dict. Persistent the first time.
                for dq in (queue_bath, queue_pump, queue_spec, queue_amcu):
                    while True:
                        # ensure that *something* gets popped out so the dict is complete
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass

                # set temp via topside PID
                while not isclose(pid.setpoint, state_curr['T_set']):
                    print("setting temperature to {}˚C".format(
                        state_curr['T_set']),
                          file=stderr,
                          end=' ')
                    # pid object is picked up by poll()
                    pid.setpoint = state_curr['T_set']
                    print('√', file=stderr)

                # set pressure persistently
                pump_free.clear()
                while not isclose(pump.press_set(), state_curr['P_set']):
                    print("setting pressure to {} bar".format(
                        state_curr['P_set']),
                          file=stderr,
                          end=' ')
                    if pump.press_set(state_curr['P_set']):
                        print('√', file=stderr)
                pump_free.set()

                ## set the excitation wavelength
                #while not isclose(spec.ex_wl(), state_curr['wl_ex']):
                #    print("setting excitation WL to {} nm".format(state_curr['wl_ex']), file=stderr, end=' ')
                #    if spec.ex_wl(state_curr['wl_ex']): print('√', file=stderr)
                #
                ## set the emission wavelength
                #while not isclose(spec.em_wl(), state_curr['wl_em']):
                #    print("setting emission WL to {} nm".format(state_curr['wl_em']), file=stderr, end=' ')
                #    if spec.ex_wl(state_curr['wl_em']): print('√', file=stderr)

                # temporary WL setters
                # Persistence implemented over cycles to improve efficiency;
                # note that this checks the previous data row.
                if not ((state_curr['wl_ex'] == data_dict['wl_ex']) and
                        (state_curr['wl_em'] == data_dict['wl_em'])):
                    #if not (isclose(spec.ex_wl(), state_curr['wl_ex']) and isclose(spec.em_wl(), state_curr['wl_em'])):
                    if (state_curr['wl_ex'] == 350) and (state_curr['wl_em']
                                                         == 428):
                        print("setting wavelengths for DPH",
                              file=stderr,
                              end=' ')
                        spec_free.clear()
                        spec.wl_set_dph()
                        spec_free.set()
                        print('√', file=stderr)

                # set polarizers
                if not ((state_curr['pol_ex'] == data_dict['pol_ex']) and
                        (state_curr['pol_em'] == data_dict['pol_em'])):
                    amcu_free.clear()
                    # take the line from other thread!
                    amcu.__ser__.send_break(duration=amcu.__ser__.timeout +
                                            0.1)
                    time.sleep(amcu.__ser__.timeout + 0.1)
                    # excitation
                    if state_curr['pol_ex'] != data_dict['pol_ex']:
                        print("setting ex polarization to {}".format(
                            state_curr['pol_ex']),
                              file=stderr,
                              end=' ')
                        while not state_curr['pol_ex'] == amcu.ex(
                                state_curr['pol_ex']):
                            pass
                        print('√', file=stderr)
                    # emission
                    if state_curr['pol_em'] != data_dict['pol_em']:
                        print("setting em polarization to {}".format(
                            state_curr['pol_em']),
                              file=stderr,
                              end=' ')
                        while not state_curr['pol_em'] == amcu.em(
                                state_curr['pol_em']):
                            pass
                        print('√', file=stderr)
                    amcu_free.set()
                    # wait until wheel is solidly in position before writing any data
                    time.sleep(amcu.__ser__.timeout)

                # init a log table for the state
                trails = pd.DataFrame(columns=list_head)

                while True:

                    time_cycle = time.time()

                    # DATA FIRST
                    for dq in (queue_bath, queue_pump, queue_spec, queue_amcu):
                        try:
                            data_dict.update(dq.popleft())
                            break
                        except:
                            pass
                    # add internal data
                    data_dict.update({
                        "clock": time.strftime("%Y%m%d %H%M%S"),
                        "watch": time.time() - time_start,
                        "state": state_num,
                        "T_set": state_curr["T_set"],
                        "P_set": state_curr["P_set"],
                        "msg": state_curr["msg"]
                    })

                    # SAFETY SECOND
                    # check for pressure system leak
                    if (data_dict["vol"] - vol_start) > args["vol_diff"]:
                        pump_free.clear()
                        pump.clear()
                        raise Exception("Pump has discharged > {} mL!".format(
                            args["vol_diff"]))

                    # control the air system
                    # if it's cold and air is off
                    if (data_dict['T_act'] <= data_dict["dewpt"] +
                            args["dew_tol"]) and not data_dict['air']:
                        pump_free.clear()
                        if waited: print(file=stderr)
                        print("\nturning air ON", file=stderr, end=' ')
                        while not pump.digital(0, 1):
                            pass
                        print("√", file=stderr)
                        pump_free.set()
                        data_dict['air'] = True
                    # if it's warm and the air is on
                    elif (data_dict['T_act'] >
                          (dewpt(data_dict['H_amb'], data_dict['T_amb']) +
                           args["dew_tol"])) and data_dict['air']:
                        # and air is on
                        pump_free.clear()
                        if waited: print(file=stderr)
                        print("\nturning air OFF", file=stderr, end=' ')
                        while not pump.digital(0, 0):
                            pass
                        print("√", file=stderr)
                        pump_free.set()
                        data_dict['air'] = False

                    # does the state change require equilibration?
                    vars_wait = [var for var in args["eqls"] if chg_prev[var]]
                    # if this in an empty dict, all(in_range.values()) will be true
                    in_range = {var: False for var in vars_wait}

                    # put new data into a trailing buffer
                    trails = trails.append(data_dict, ignore_index=True)
                    # cut the buffer down to the min equil time of the slowest variable
                    if vars_wait:
                        trails = trails[trails['watch'] >= (
                            trails['watch'].iloc[-1] -
                            max([min(args["eqls"][var])
                                 for var in vars_wait]))]

                    # if the fluor reading has changed, write line to logfile
                    # this check takes about 0.1 ms
                    if (trails.shape[0]
                            == 1) or (trails["intensity"].iloc[-1] !=
                                      trails["intensity"].iloc[-2]):
                        # write data to file
                        hand_log.write('\t'.join(
                            [str(data_dict[col]) for col in list_head]) + '\n')
                        hand_log.flush()

                    for var in vars_wait:
                        # if variable's timeout is past
                        if (time_cycle - time_state) >= max(args["eqls"][var]):
                            in_range[var] = True
                        # else, if min equilibration has elapsed
                        elif (time_cycle - time_state) >= min(
                                args["eqls"][var]):
                            #print("{}: {}\r".format(var, data_dict[var]), end='')
                            # see if the trace of the variable is in range
                            trace = trails[trails['watch'] >= (
                                trails['watch'].iloc[-1] -
                                min(args["eqls"][var]))][
                                    setp2meas[var]].tolist()
                            #print(trace)
                            # and green- or redlight the variable as appropriate
                            in_range[var] = ((max(trace) <
                                              (state_curr[var] +
                                               args["tols"][setp2meas[var]]))
                                             and
                                             (min(trace) >
                                              (state_curr[var] -
                                               args["tols"][setp2meas[var]])))

                    # if all equilibrations have cleared
                    if all(in_range.values()):

                        if waited: print(file=stderr)  # newline
                        waited = False

                        # open the shutter
                        if (not readings) and args["auto_shut"] and len(
                                vars_wait) and not sat:
                            spec_free.clear()
                            while not spec.shutter(True):
                                pass
                            spec_free.set()
                            time_open = time.time()

                        # let the dye relax after a long dark period (temp xsition)
                        if ("T_set" not in vars_wait) or (
                            (time.time() - args["shut_sit"]) >= time_open):
                            # take some readings
                            if (trails.shape[0]
                                    == 1) or (trails["intensity"].iloc[-1] !=
                                              trails["intensity"].iloc[-2]):
                                if readings:
                                    print("reading {}: {} AU \r".format(
                                        readings,
                                        trails['intensity'].iloc[-1]),
                                          end='',
                                          file=stderr)
                                readings += 1
                            # break out of loop to next state
                            if (readings > args["n_read"]):
                                if args["auto_shut"] and any([
                                        chg_next[var]
                                        for var in args["eqls"].keys()
                                ]):
                                    spec_free.clear()
                                    while not spec.shutter(False):
                                        pass
                                    spec_free.set()
                                print(file=stderr)
                                break
                        else:
                            print("dye relaxed for {}/{} s\r".format(
                                round(time.time() - time_open),
                                args["shut_sit"]),
                                  end='',
                                  file=stderr)
                            sat = True
                            # gotta get a newline in here somewhere!
                    else:
                        # what are we waiting for?
                        print("waiting {} s to get {} s of stability\r".format(
                            round(time.time() - time_state),
                            max([min(args["eqls"][var])
                                 for var in vars_wait])),
                              end='',
                              file=stderr)
                        waited = True

                    # prescribed sleep
                    try:
                        time.sleep((args["cycle_time"] / 1000) -
                                   (time.time() - (time_cycle)))
                    except:
                        pass

            # shut down when done
            pump_free.clear()
            pump.digital(0, 0)
            pump.pause()
            pump.disconnect()
            bath_free.clear()
            bath.on(False)
            bath.disconnect()
            spec_free.clear()
            spec.shutter(True)
            spec.disconnect()
            amcu_free.clear()
            amcu.__ser__.send_break(duration=amcu.__ser__.timeout + 0.1)
            time.sleep(amcu.__ser__.timeout + 0.1)
            amcu.lamp(False)
            amcu.ex('0')
            amcu.em('0')
            sys.exit(0)

        except:
            pump_free.clear()
            pump.pause()
            pump.digital(0, 0)  # turn the air off!
            spec_free.clear()
            spec.ack()
            spec.shutter(False)
            amcu_free.clear()
            amcu.__ser__.send_break(duration=amcu.__ser__.timeout + 0.1)
            time.sleep(amcu.__ser__.timeout + 0.1)
            amcu.lamp(False)
            amcu.ex('0')
            amcu.em('0')
            traceback.print_exc()
예제 #4
0
#!/usr/bin/python3
"""
initialize Isotemp water bath, then provide user with an interactive console for debugging
"""

import rf5301
import traceback

spec = rf5301.RF5301(port="/dev/cu.usbserial-FTV5C58R0")

while True:
    cmd = input("spec.")
    try:
        ret = eval("spec.{}".format(cmd))
        print(ret)
    except:
        spec.__ser__.flush()
        traceback.print_exc()