From 15e041229b2c352aac65467519cf493acca633a2 Mon Sep 17 00:00:00 2001 From: Zoltan Tuza Date: Mon, 3 Feb 2020 19:38:33 +0000 Subject: [PATCH 1/8] - adding device number to the error messages - creating a placeholder for DeviceName for later use --- app.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app.py b/app.py index 2c8d74e..d383924 100644 --- a/app.py +++ b/app.py @@ -34,6 +34,7 @@ 'presentDevices' : { 'M0' : 0,'M1' : 0,'M2' : 0,'M3' : 0,'M4' : 0,'M5' : 0,'M6' : 0,'M7' : 0}, 'Version' : {'value' : 'Turbidostat V3.0'}, 'DeviceID' : '', + 'DeviceName': '', 'time' : {'record' : []}, 'LEDA' : {'WL' : '395', 'default': 0.1, 'target' : 0.0, 'max': 1.0, 'min' : 0.0,'ON' : 0}, 'LEDB' : {'WL' : '457', 'default': 0.1, 'target' : 0.0, 'max': 1.0, 'min' : 0.0,'ON' : 0}, @@ -370,10 +371,9 @@ def initialise(M): scanDevices(M) if(sysData[M]['present']==1): turnEverythingOff(M) - print(str(datetime.now()) + " Initialised " + str(M) +', Device ID: ' + sysData[M]['DeviceID']) + print(str(datetime.now()) + " Initialised " + str(M) + ', (' + sysData[M]['DeviceName'] + ') Device ID: ' + + sysData[M]['DeviceID']) - - def initialiseAll(): # Initialisation function which runs at when software is started for the first time. @@ -584,12 +584,12 @@ def SetOutputOn(M,item,force): if (force==1): sysData[M][item]['ON']=1 SetOutput(M,item) - return ('', 204) + return ('', 204) elif(force==0): sysData[M][item]['ON']=0; SetOutput(M,item) - return ('', 204) + return ('', 204) #Elsewise this is doing a flip operation (i.e. changes to opposite state to that which it is currently in) if (sysData[M][item]['ON']==0): @@ -1336,7 +1336,7 @@ def I2CCom(M,device,rw,hl,data1,data2,SMBUSFLAG): global sysDevices if(sysData[M]['present']==0): #Something stupid has happened in software if this is the case! - print(str(datetime.now()) + ' Trying to communicate with absent device - bug in software!. Disabling hardware and software!') + print(str(datetime.now()) + ' Trying to communicate with M%s absent device - bug in software!. Disabling hardware and software!' % M) sysItems['Watchdog']['ON']=0 #Basically this will crash all the electronics and the software. out=0 tries=-1 From 6e324ef79ff4c0d1a24fdf4cbf019bb32ba45566 Mon Sep 17 00:00:00 2001 From: zoltuz Date: Tue, 4 Feb 2020 11:51:49 +0000 Subject: [PATCH 2/8] - reading config file - check config file for config key, raise ValueError if critical config data is missing - number of OD measurements set by the config file --- app.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index d383924..10af494 100644 --- a/app.py +++ b/app.py @@ -21,6 +21,7 @@ application = Flask(__name__) +application.config.from_pyfile('config/chibio_default.cfg', silent=True) application.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 #Try this https://stackoverflow.com/questions/23112316/using-flask-how-do-i-modify-the-cache-control-header-for-all-output/23115561#23115561 lock=Lock() @@ -2066,13 +2067,14 @@ def runExperiment(M,placeholder): sysData[M]['OD']['Measuring']=1 #Begin measuring - this flag is just to indicate that a measurement is currently being taken. - #We now meausre OD 4 times and take the average to reduce noise when in auto mode! - ODV=0.0 - for i in [0, 1, 2, 3]: + # We now measure OD N times and take the average to reduce noise when in auto mode! + check_config_value(config_key='NUMBER_OF_OD_MEASUREMENTS',default_value=4) + ODV = 0.0 + for _ in range(0, application.config['NUMBER_OF_OD_MEASUREMENTS']-1): MeasureOD(M) ODV=ODV+sysData[M]['OD']['current'] time.sleep(0.25) - sysData[M]['OD']['current']=ODV/4.0 + sysData[M]['OD']['current'] = ODV/float(application.config['NUMBER_OF_OD_MEASUREMENTS']) MeasureTemp(M,'Internal') #Measuring all temperatures MeasureTemp(M,'External') @@ -2187,7 +2189,12 @@ def runExperiment(M,placeholder): addTerminal(M,'Experiment Stopped') - +def check_config_value(config_key, default_value, critical=False): + if config_key not in application.config.keys(): + if critical: + raise ValueError('config value for %s was not found, it must be set for operations' % config_key) + application.config[config_key] = default_value + application.logger.warning('config value %s was not found, set to default: %d' % (config_key, default_value)) if __name__ == '__main__': initialiseAll() From fc5e468a894e59163fee93533b08a52d42c9ab22 Mon Sep 17 00:00:00 2001 From: zoltuz Date: Tue, 4 Feb 2020 14:53:14 +0000 Subject: [PATCH 3/8] - adding configuration for two/four pump per chi.bio unit, this allows to run two chi.bio units per pump board --- app.py | 56 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/app.py b/app.py index 10af494..e7f36e5 100644 --- a/app.py +++ b/app.py @@ -406,7 +406,13 @@ def turnEverythingOff(M): I2CCom(M,'DAC',0,8,int('00000000',2),int('00000000',2),0)#Sets all DAC Channels to zero!!! setPWM(M,'PWM',sysItems['All'],0,0) - setPWM(M,'Pumps',sysItems['All'],0,0) + + if application.config['TWO_PUMP_PER_DEVICE']: + chibios_to_shut_down = [0, 1, 2, 3] + else: + chibios_to_shut_down = [0, 1, 2, 4, 5, 6, 7] + if int(M[1]) in chibios_to_shut_down: + setPWM(M=M, device='Pumps', channels=sysItems['All'], fraction=0, ConsecutiveFails=0) SetOutputOn(M,'Stir',0) SetOutputOn(M,'Thermostat',0) @@ -682,7 +688,29 @@ def PumpModulation(M,item): global sysData global sysItems global sysDevices - + + check_config_value(config_key='TWO_PUMP_PER_DEVICE', default_value=False) + + if application.config['TWO_PUMP_PER_DEVICE']: + pump_mapping = {'M4': 'M0', 'M5': 'M1', 'M6': 'M2', 'M7': 'M3'} + if int(M[1]) in [0, 1, 2 , 3]: + if item == 'Pump1' or item == 'Pump2': + MB = M + itemB = item + elif item == 'Pump3' or item == 'Pump4': + sysData[M][item]['ON'] = 0 + return + else: + if item == 'Pump1' or item == 'Pump2': + MB = pump_mapping[M] + itemB = 'Pump' + str(int(item[4])+2) + elif item == 'Pump3' or item == 'Pump4': + sysData[M][item]['ON'] = 0 + return + else: + MB = M + itemB = item + sysDevices[M][item]['threadCount']=(sysDevices[M][item]['threadCount']+1)%100 #Index of the particular thread running. currentThread=sysDevices[M][item]['threadCount'] @@ -691,10 +719,10 @@ def PumpModulation(M,item): if (abs(sysData[M][item]['target']*sysData[M][item]['ON'])!=1 and currentThread==sysDevices[M][item]['threadCount']): #In all cases we turn things off to begin sysDevices[M][item]['active']=1 - setPWM(M,'Pumps',sysItems[item]['In1'],0.0*float(sysData[M][item]['ON']),0) - setPWM(M,'Pumps',sysItems[item]['In2'],0.0*float(sysData[M][item]['ON']),0) - setPWM(M,'Pumps',sysItems[item]['In1'],0.0*float(sysData[M][item]['ON']),0) - setPWM(M,'Pumps',sysItems[item]['In2'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In1'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In2'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In1'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In2'],0.0*float(sysData[M][item]['ON']),0) sysDevices[M][item]['active']=0 if (sysData[M][item]['ON']==0): return @@ -712,23 +740,23 @@ def PumpModulation(M,item): if (sysData[M][item]['target']>0 and currentThread==sysDevices[M][item]['threadCount']): #Turning on pumps in forward direction sysDevices[M][item]['active']=1 - setPWM(M,'Pumps',sysItems[item]['In1'],1.0*float(sysData[M][item]['ON']),0) - setPWM(M,'Pumps',sysItems[item]['In2'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In1'],1.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In2'],0.0*float(sysData[M][item]['ON']),0) sysDevices[M][item]['active']=0 elif (sysData[M][item]['target']<0 and currentThread==sysDevices[M][item]['threadCount']): #Or backward direction. sysDevices[M][item]['active']=1 - setPWM(M,'Pumps',sysItems[item]['In1'],0.0*float(sysData[M][item]['ON']),0) - setPWM(M,'Pumps',sysItems[item]['In2'],1.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In1'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In2'],1.0*float(sysData[M][item]['ON']),0) sysDevices[M][item]['active']=0 time.sleep(Ontime) if(abs(sysData[M][item]['target'])!=1 and currentThread==sysDevices[M][item]['threadCount']): #Turning off pumps at appropriate time. sysDevices[M][item]['active']=1 - setPWM(M,'Pumps',sysItems[item]['In1'],0.0*float(sysData[M][item]['ON']),0) - setPWM(M,'Pumps',sysItems[item]['In2'],0.0*float(sysData[M][item]['ON']),0) - setPWM(M,'Pumps',sysItems[item]['In1'],0.0*float(sysData[M][item]['ON']),0) - setPWM(M,'Pumps',sysItems[item]['In2'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In1'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In2'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In1'],0.0*float(sysData[M][item]['ON']),0) + setPWM(MB,'Pumps',sysItems[itemB]['In2'],0.0*float(sysData[M][item]['ON']),0) sysDevices[M][item]['active']=0 Time2=datetime.now() From 4b88e1f6ec6ca44aa133cc33fd228c0b0a984514 Mon Sep 17 00:00:00 2001 From: zoltuz Date: Tue, 4 Feb 2020 14:55:46 +0000 Subject: [PATCH 4/8] - default config file --- config/chibio_default.cfg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 config/chibio_default.cfg diff --git a/config/chibio_default.cfg b/config/chibio_default.cfg new file mode 100644 index 0000000..60ebea0 --- /dev/null +++ b/config/chibio_default.cfg @@ -0,0 +1,4 @@ +NUMBER_OF_OD_MEASUREMENTS = 4 +TWO_PUMP_PER_DEVICE = False +PUMP_COMM_FAILURE_THRESHOLD = 10 +CONTINUOUS_STIRRING = False \ No newline at end of file From 45c6044be23d647168d27298aa5dca45cfe62b8c Mon Sep 17 00:00:00 2001 From: zoltuz Date: Wed, 5 Feb 2020 10:53:45 +0000 Subject: [PATCH 5/8] - add logging event for successful config value check --- app.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index e7f36e5..c513e75 100644 --- a/app.py +++ b/app.py @@ -2220,9 +2220,11 @@ def runExperiment(M,placeholder): def check_config_value(config_key, default_value, critical=False): if config_key not in application.config.keys(): if critical: - raise ValueError('config value for %s was not found, it must be set for operations' % config_key) + raise ValueError('config value for %s was not found, it must be set for safe operations' % config_key) application.config[config_key] = default_value - application.logger.warning('config value %s was not found, set to default: %d' % (config_key, default_value)) + application.logger.warning('config value %s was not found and set to default: %d' % (config_key, default_value)) + else: + application.logger.info('config found: %s=%s' % (config_key, application.config[config_key])) if __name__ == '__main__': initialiseAll() From 0b5dd84a1067c77b79737b8067c58aa3a699d476 Mon Sep 17 00:00:00 2001 From: zoltuz Date: Thu, 6 Feb 2020 17:55:28 +0000 Subject: [PATCH 6/8] - fixing typos - moving the config value to the initialization phase --- app.py | 12 ++++++------ config/chibio_default.cfg | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index c513e75..de6b60b 100644 --- a/app.py +++ b/app.py @@ -383,6 +383,9 @@ def initialiseAll(): time.sleep(2.0) #This wait is to allow the watchdog circuit to boot. print(str(datetime.now()) + ' Initialising devices') + check_config_value(config_key='TWO_PUMPS_PER_DEVICE', default_value=False) + check_config_value(config_key='NUMBER_OF_OD_MEASUREMENTS', default_value=4) + for M in ['M0','M1','M2','M3','M4','M5','M6','M7']: initialise(M) scanDevices("all") @@ -407,7 +410,7 @@ def turnEverythingOff(M): I2CCom(M,'DAC',0,8,int('00000000',2),int('00000000',2),0)#Sets all DAC Channels to zero!!! setPWM(M,'PWM',sysItems['All'],0,0) - if application.config['TWO_PUMP_PER_DEVICE']: + if application.config['TWO_PUMPS_PER_DEVICE']: chibios_to_shut_down = [0, 1, 2, 3] else: chibios_to_shut_down = [0, 1, 2, 4, 5, 6, 7] @@ -689,11 +692,9 @@ def PumpModulation(M,item): global sysItems global sysDevices - check_config_value(config_key='TWO_PUMP_PER_DEVICE', default_value=False) - - if application.config['TWO_PUMP_PER_DEVICE']: + if application.config['TWO_PUMPS_PER_DEVICE']: pump_mapping = {'M4': 'M0', 'M5': 'M1', 'M6': 'M2', 'M7': 'M3'} - if int(M[1]) in [0, 1, 2 , 3]: + if int(M[1]) in [0, 1, 2, 3]: if item == 'Pump1' or item == 'Pump2': MB = M itemB = item @@ -2096,7 +2097,6 @@ def runExperiment(M,placeholder): sysData[M]['OD']['Measuring']=1 #Begin measuring - this flag is just to indicate that a measurement is currently being taken. # We now measure OD N times and take the average to reduce noise when in auto mode! - check_config_value(config_key='NUMBER_OF_OD_MEASUREMENTS',default_value=4) ODV = 0.0 for _ in range(0, application.config['NUMBER_OF_OD_MEASUREMENTS']-1): MeasureOD(M) diff --git a/config/chibio_default.cfg b/config/chibio_default.cfg index 60ebea0..83f5c9f 100644 --- a/config/chibio_default.cfg +++ b/config/chibio_default.cfg @@ -1,4 +1,4 @@ NUMBER_OF_OD_MEASUREMENTS = 4 -TWO_PUMP_PER_DEVICE = False +TWO_PUMPS_PER_DEVICE = False PUMP_COMM_FAILURE_THRESHOLD = 10 CONTINUOUS_STIRRING = False \ No newline at end of file From eedd544b11912c2c25f3d3e50c3fdd9fa165d817 Mon Sep 17 00:00:00 2001 From: zoltuz Date: Thu, 6 Feb 2020 20:41:23 +0000 Subject: [PATCH 7/8] - making the device comm failure threshold configurable --- app.py | 7 +++++-- config/chibio_default.cfg | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index de6b60b..dd61bc4 100644 --- a/app.py +++ b/app.py @@ -385,6 +385,8 @@ def initialiseAll(): check_config_value(config_key='TWO_PUMPS_PER_DEVICE', default_value=False) check_config_value(config_key='NUMBER_OF_OD_MEASUREMENTS', default_value=4) + check_config_value(config_key='DEVICE_COMM_FAILURE_THRESHOLD', default_value=10) + for M in ['M0','M1','M2','M3','M4','M5','M6','M7']: initialise(M) @@ -1450,11 +1452,12 @@ def I2CCom(M,device,rw,hl,data1,data2,SMBUSFLAG): out=0 sysData[M]['present']=0 tries=-1 - if tries>10: #In this case something else has gone wrong, so we panic. + if tries >= application.config['DEVICE_COMM_FAILURE_THRESHOLD']: #In this case something else has gone wrong, so we panic. sysItems['Watchdog']['ON']=0 #Basically this will crash all the electronics and the software. out=0 sysData[M]['present']=0 - print('Failed to communicate to a device 10 times. Disabling hardware and software!') + print('Failed to communicate to a device %d times. Disabling hardware and software!' % + application.config['DEVICE_COMM_FAILURE_THRESHOLD']) tries=-1 os._exit(4) diff --git a/config/chibio_default.cfg b/config/chibio_default.cfg index 83f5c9f..89e27ca 100644 --- a/config/chibio_default.cfg +++ b/config/chibio_default.cfg @@ -1,4 +1,4 @@ NUMBER_OF_OD_MEASUREMENTS = 4 TWO_PUMPS_PER_DEVICE = False -PUMP_COMM_FAILURE_THRESHOLD = 10 +DEVICE_COMM_FAILURE_THRESHOLD = 10 CONTINUOUS_STIRRING = False \ No newline at end of file From 77b31b96dabd1f06f1485738e469539b490ef8db Mon Sep 17 00:00:00 2001 From: zoltuz Date: Mon, 10 Feb 2020 11:35:45 +0000 Subject: [PATCH 8/8] - bug fix for number of OD measurement --- app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index dd61bc4..954c18b 100644 --- a/app.py +++ b/app.py @@ -2101,7 +2101,7 @@ def runExperiment(M,placeholder): # We now measure OD N times and take the average to reduce noise when in auto mode! ODV = 0.0 - for _ in range(0, application.config['NUMBER_OF_OD_MEASUREMENTS']-1): + for _ in range(0, application.config['NUMBER_OF_OD_MEASUREMENTS']): MeasureOD(M) ODV=ODV+sysData[M]['OD']['current'] time.sleep(0.25)