Skip to content

Commit

Permalink
Merge pull request #7 from mgalardini/new-server
Browse files Browse the repository at this point in the history
New client for new server/arduino configuration
  • Loading branch information
cpmancuso authored Jul 24, 2019
2 parents 300c914 + 9ad260c commit f27744b
Show file tree
Hide file tree
Showing 4 changed files with 655 additions and 521 deletions.
123 changes: 76 additions & 47 deletions experiment/template/custom_script.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import eVOLVER_module
#!/usr/bin/env python3

import numpy as np
import logging
import os.path
import time

# logger setup
logger = logging.getLogger(__name__)

##### USER DEFINED GENERAL SETTINGS #####

#set new name for each experiment, otherwise files will be overwritten
EXP_NAME = 'test_expt'
GUI_NAME = 'eVOLVER Experiment'
EVOLVER_IP = '192.168.1.2'
EVOLVER_PORT = 8081

##### Identify pump calibration files, define initial values for temperature, stirring, volume, power settings

TEMP_INITIAL = [30] * 16 #degrees C, makes 16-value list
TEMP_INITIAL = [30] * 16 #degrees C, makes 16-value list
#Alternatively enter 16-value list to set different values
#TEMP_INITIAL = [30,30,30,30,32,32,32,32,34,34,34,34,36,36,36,36]

Expand All @@ -24,12 +28,13 @@
VOLUME = 25 #mL, determined by vial cap straw length
OD_POWER = 2125 #must match value used for OD calibration
PUMP_CAL_FILE = 'pump_cal.txt' #tab delimited, mL/s with 16 influx pumps on first row, etc.
OPERATION_MODE = 'chemostat' #use to choose between 'turbidostat' and 'chemostat' functions
OPERATION_MODE = 'turbidostat' #use to choose between 'turbidostat' and 'chemostat' functions
# if using a different mode, name your function as the OPERATION_MODE variable

##### END OF USER DEFINED GENERAL SETTINGS #####


def turbidostat (OD_data, temp_data, vials, elapsed_time):
def turbidostat(eVOLVER, input_data, vials, elapsed_time):
OD_data = input_data['transformed']['od_90']

##### USER DEFINED VARIABLES #####

Expand All @@ -39,7 +44,7 @@ def turbidostat (OD_data, temp_data, vials, elapsed_time):
lower_thresh = [0.2] * len(vials) #to set all vials to the same value, creates 16-value list
upper_thresh = [0.4] * len(vials) #to set all vials to the same value, creates 16-value list

#Alternatively, use 16 value list to set different thresholds, use 9999 for vials not being used
#Alternatively, use 16 value list to set different thresholds, use 9999 for vials not being used
#lower_thresh = [0.2, 0.2, 0.3, 0.3, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999]
#upper_thresh = [0.4, 0.4, 0.4, 0.4, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999]

Expand All @@ -51,37 +56,38 @@ def turbidostat (OD_data, temp_data, vials, elapsed_time):
#Tunable settings for overflow protection, pump scheduling etc. Unlikely to change between expts

time_out = 5 #(sec) additional amount of time to run efflux pump
pump_wait = 3 # (min) minimum amount of time to wait between pump events
pump_wait = 3 # (min) minimum amount of time to wait between pump events

##### End of Turbidostat Settings #####

control = np.power(2,range(0,32)) #vial addresses
save_path = os.path.dirname(os.path.realpath(__file__)) #save path
flow_rate = eVOLVER_module.get_flow_rate() #read from calibration file
flow_rate = eVOLVER.get_flow_rate() #read from calibration file


##### Turbidostat Control Code Below #####

# fluidic message: initialized so that no change is sent
MESSAGE = ['--'] * 48
for x in turbidostat_vials: #main loop through each vial

# Update turbidostat configuration files for each vial
# initialize OD and find OD path

file_name = "vial{0}_ODset.txt".format(x)
ODset_path = os.path.join(save_path,EXP_NAME,'ODset',file_name)
ODset_path = os.path.join(save_path, EXP_NAME, 'ODset', file_name)
data = np.genfromtxt(ODset_path, delimiter=',')
ODset = data[len(data)-1][1]
ODsettime = data[len(data)-1][0]
num_curves=len(data)/2;

file_name = "vial{0}_OD.txt".format(x)
OD_path = os.path.join(save_path,EXP_NAME,'OD',file_name)
OD_path = os.path.join(save_path, EXP_NAME, 'OD', file_name)
data = np.genfromtxt(OD_path, delimiter=',')
average_OD = 0

# Determine whether turbidostat dilutions are needed
enough_ODdata = (len(data) > 7) #logical, checks to see if enough data points (couple minutes) for sliding window
collecting_more_curves = (num_curves <= (stop_after_n_curves+2)) #logical, checks to see if enough growth curves have happened
collecting_more_curves = (num_curves <= (stop_after_n_curves + 2)) #logical, checks to see if enough growth curves have happened

if enough_ODdata:
# Take median to avoid outlier
Expand All @@ -92,16 +98,17 @@ def turbidostat (OD_data, temp_data, vials, elapsed_time):

#if recently exceeded upper threshold, note end of growth curve in ODset, allow dilutions to occur and growthrate to be measured
if (average_OD > upper_thresh[x]) and (ODset != lower_thresh[x]):
text_file = open(ODset_path,"a+")
text_file.write("{0},{1}\n".format(elapsed_time, lower_thresh[x]))
text_file = open(ODset_path, "a+")
text_file.write("{0},{1}\n".format(elapsed_time,
lower_thresh[x]))
text_file.close()
ODset = lower_thresh[x]
# calculate growth rate
eVOLVER_module.calc_growth_rate(x, ODsettime, elapsed_time)
eVOLVER.calc_growth_rate(x, ODsettime, elapsed_time)

#if have approx. reached lower threshold, note start of growth curve in ODset
if (average_OD < (lower_thresh[x]+(upper_thresh[x] - lower_thresh[x])/3)) and (ODset != upper_thresh[x]):
text_file = open(ODset_path,"a+")
if (average_OD < (lower_thresh[x] + (upper_thresh[x] - lower_thresh[x]) / 3)) and (ODset != upper_thresh[x]):
text_file = open(ODset_path, "a+")
text_file.write("{0},{1}\n".format(elapsed_time, upper_thresh[x]))
text_file.close()
ODset = upper_thresh[x]
Expand All @@ -116,21 +123,38 @@ def turbidostat (OD_data, temp_data, vials, elapsed_time):

time_in = round(time_in, 2)

save_path = os.path.dirname(os.path.realpath(__file__))
file_name = "vial{0}_pump_log.txt".format(x)
file_path = os.path.join(save_path,EXP_NAME,'pump_log',file_name)
file_path = os.path.join(save_path, EXP_NAME,
'pump_log', file_name)
data = np.genfromtxt(file_path, delimiter=',')
last_pump = data[len(data)-1][0]
if ((elapsed_time - last_pump)*60) >= pump_wait: # if sufficient time since last pump, send command to Arduino
MESSAGE = {'pumps_binary':"{0:b}".format(control[x]), 'pump_time': time_in, 'efflux_pump_time': time_out, 'delay_interval': 0, 'times_to_repeat': 0, 'run_efflux': 1}
eVOLVER_module.fluid_command(MESSAGE, x, elapsed_time, pump_wait *60, time_in, 'y')
logger.info('turbidostat dilution for vial %d' % x)
# influx pump
MESSAGE[x] = str(time_in)
# efflux pump
MESSAGE[x + 16] = str(time_in + time_out)

file_name = "vial{0}_pump_log.txt".format(x)
file_path = os.path.join(save_path, EXP_NAME, 'pump_log', file_name)

text_file = open(file_path, "a+")
text_file.write("{0},{1}\n".format(elapsed_time, time_in))
text_file.close()
else:
logger.debug('not enough OD measurements for vial %d' % x)

# send fluidic command only if we are actually turning on any of the pumps
if MESSAGE != ['--'] * 48:
eVOLVER.fluid_command(MESSAGE)

# your_FB_function_here() #good spot to call feedback functions for dynamic temperature, stirring, etc for ind. vials
# your_function_here() #good spot to call non-feedback functions for dynamic temperature, stirring, etc.

# end of turbidostat() fxn

def chemostat (OD_data, temp_data, vials, elapsed_time):
def chemostat(eVOLVER, input_data, vials, elapsed_time):
OD_data = input_data['transformed']['od_90']

##### USER DEFINED VARIABLES #####
start_OD = 0 # ~OD600, set to 0 to start chemostate dilutions at any positive OD
Expand All @@ -139,10 +163,10 @@ def chemostat (OD_data, temp_data, vials, elapsed_time):

chemostat_vials = vials #vials is all 16, can set to different range (ex. [0,1,2,3]) to only trigger tstat on those vials

rate_config = [0.5] * len(vials) #to set all vials to the same value, creates 16-value list
rate_config = [0.5] * 16 #to set all vials to the same value, creates 16-value list
#UNITS of 1/hr, NOT mL/hr, rate = flowrate/volume, so dilution rate ~ growth rate, set to 0 for unused vials

#Alternatively, use 16 value list to set different rates, use 0 for vials not being used
#Alternatively, use 16 value list to set different rates, use 0 for vials not being used
#rate_config = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5,1.6]

##### END OF USER DEFINED VARIABLES #####
Expand All @@ -155,32 +179,21 @@ def chemostat (OD_data, temp_data, vials, elapsed_time):

##### End of Chemostat Settings #####

control = np.power(2,range(0,32)) #vial addresses

save_path = os.path.dirname(os.path.realpath(__file__)) #save path
flow_rate = eVOLVER_module.get_flow_rate() #read from calibration file
flow_rate = eVOLVER.get_flow_rate() #read from calibration file
period_config = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] #initialize array
bolus_in_s = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] #initialize array


##### Chemostat Control Code Below #####

for x in chemostat_vials: #main loop through each vial

#calculate time needed to pump bolus for each pump
bolus_in_s[x] = bolus/flow_rate[x]

# calculate the period (i.e. frequency of dilution events) based on user specified growth rate and bolus size
if rate_config[x] > 0:
period_config[x] = (3600*bolus)/((rate_config[x])*VOLUME) #scale dilution rate by bolus size and volume
else: # if no dilutions needed, then just loops with no dilutions
period_config[x] = 0

# Update chemostat configuration files for each vial

#initialize OD and find OD path
file_name = "vial{0}_OD.txt".format(x)
OD_path = os.path.join(save_path,EXP_NAME,'OD',file_name)
OD_path = os.path.join(save_path, EXP_NAME, 'OD', file_name)
data = np.genfromtxt(OD_path, delimiter=',')
average_OD = 0
enough_ODdata = (len(data) > 7) #logical, checks to see if enough data points (couple minutes) for sliding window
Expand All @@ -189,13 +202,14 @@ def chemostat (OD_data, temp_data, vials, elapsed_time):

#calculate median OD
od_values_from_file = []
for n in range(1,7):
for n in range(1, 7):
od_values_from_file.append(data[len(data)-n][1])
average_OD = float(np.median(od_values_from_file))

# set chemostat config path and pull current state from file
file_name = "vial{0}_chemoconfig.txt".format(x)
chemoconfig_path = os.path.join(save_path,EXP_NAME,'chemo_config',file_name)
file_name = "vial{0}_chemo_config.txt".format(x)
chemoconfig_path = os.path.join(save_path, EXP_NAME,
'chemo_config', file_name)
chemo_config = np.genfromtxt(chemoconfig_path, delimiter=',')
last_chemoset = chemo_config[len(chemo_config)-1][0] #should t=0 initially, changes each time a new command is written to file
last_chemophase = chemo_config[len(chemo_config)-1][1] #should be zero initially, changes each time a new command is written to file
Expand All @@ -204,20 +218,35 @@ def chemostat (OD_data, temp_data, vials, elapsed_time):
# once start time has passed and culture hits start OD, if no command has been written, write new chemostat command to file
if ((elapsed_time > start_time) & (average_OD > start_OD)):

#calculate time needed to pump bolus for each pump
bolus_in_s[x] = bolus/flow_rate[x]

# calculate the period (i.e. frequency of dilution events) based on user specified growth rate and bolus size
if rate_config[x] > 0:
period_config[x] = (3600*bolus)/((rate_config[x])*VOLUME) #scale dilution rate by bolus size and volume
else: # if no dilutions needed, then just loops with no dilutions
period_config[x] = 0

if (last_chemorate != period_config[x]):
print('Chemostat updated in vial {0}'.format(x))
logger.info('chemostat initiated for vial %d, period %.2f'
% (x, period_config[x]))
# writes command to chemo_config file, for storage
text_file = open(chemoconfig_path,"a+")
text_file.write("{0},{1},{2}\n".format(elapsed_time,(last_chemophase+1),period_config[x])) #note that this changes chemophase
text_file = open(chemoconfig_path, "a+")
text_file.write("{0},{1},{2}\n".format(elapsed_time,
(last_chemophase+1),
period_config[x])) #note that this changes chemophase
text_file.close()
else:
logger.debug('not enough OD measurements for vial %d' % x)

# your_FB_function_here() #good spot to call feedback functions for dynamic temperature, stirring, etc for ind. vials
# your_function_here() #good spot to call non-feedback functions for dynamic temperature, stirring, etc.
# your_function_here() #good spot to call non-feedback functions for dynamic temperature, stirring, etc.

eVOLVER_module.update_chemo(vials, bolus_in_s) #uses values stored in chemo_config files
eVOLVER.update_chemo(input_data, chemostat_vials, bolus_in_s, period_config) #compares computed chemostat config to the remote one
# end of chemostat() fxn

# def your_function_here(): # good spot to define modular functions for dynamics or feedback

if __name__ == '__main__':
print('Please run main_eVOLVER.py instead')
print('Please run eVOLVER.py instead')
Loading

0 comments on commit f27744b

Please sign in to comment.