Skip to content
This repository has been archived by the owner on Mar 28, 2024. It is now read-only.

Commit

Permalink
Merge pull request #36 from psyfood/userdefined_configuration
Browse files Browse the repository at this point in the history
NF: Enable user to specify pump configuration directory
  • Loading branch information
ArndalAndersen authored Apr 29, 2019
2 parents 647f305 + 5bb833f commit 46b729e
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
in their terminal after installation, which will fire up the backend and
open the browser
- Add versioneer
- Enable user to specify pump configuration directory
- Update to `react-scripts` 2.1.3

2018.11.07
Expand Down
33 changes: 30 additions & 3 deletions pyqmix_backend/backend_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,26 @@ class SetUpConfig(Resource):

def get(self):
# Return a list of available config-dirs and send to frontend
list_of_available_configurations = config.get_available_qmix_configs()
try:
list_of_available_configurations = config.get_available_qmix_configs()
except:
list_of_available_configurations = [] # If the default configuration does not exist

if list_of_available_configurations:
configuration_path = config.DEFAULT_CONFIGS_DIR
else: # If the default configuration path does not exist or if there are no configurations
configuration_path = op.expanduser('~')
list_of_available_configurations = config.get_available_qmix_configs(configs_dir=configuration_path)

list_of_available_syringe_sizes = list(syringes.keys())

# Return status of pump-setup
config_setup = is_config_set_up()

setup_dict = {'is_config_set_up': config_setup,
'available_configs': list_of_available_configurations,
'available_syringes': list_of_available_syringe_sizes}
'available_syringes': list_of_available_syringe_sizes,
'configuration_path': configuration_path}
return setup_dict

@api.expect(config_setup)
Expand All @@ -132,6 +143,21 @@ def put(self):
set_up_config(config_name=config_name)


@api.route('/api/config_update')
class UpdateConfig(Resource):

def put(self):
payload = request.json
configuration_path = payload['configPath']
try:
list_of_available_configurations = config.get_available_qmix_configs(configs_dir=configuration_path)
except: # Return empty list if path does not exist
list_of_available_configurations = []
setup_dict = {'available_configs': list_of_available_configurations}

return setup_dict


@api.route('/api/pumps')
class InitiateOrDisconnectPumps(Resource):

Expand Down Expand Up @@ -268,6 +294,7 @@ def disconnect_pumps():
print(f'Bus after "closing": {session_paramters["bus"]}')

session_paramters['pumps'] = {}
config.delete_config()

return True

Expand Down Expand Up @@ -296,8 +323,8 @@ def get_pump_state(pump_id):

return pump_status

def pump_set_fill_level(pump_id, target_volume, flow_rate):

def pump_set_fill_level(pump_id, target_volume, flow_rate):
if app.config['test_session']:
print(f'Starting virtual pump: {pump_id} and setting '
f'target_volume to {target_volume} mL '
Expand Down
98 changes: 76 additions & 22 deletions pyqmix_frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ class PumpForm extends Component {

// System setup
webConnectedToPumps: false, // Does the website think the pumps are connected (based on user-input, not backend)
isPumpConfigSetUp: false, // Are the pumps set up in the backend
isPumpConfigSetUp: false, // Are the pumps configured in the backend
userEnteredPumpConfiguration: false, // Method to wait for user input on config-name and pump-type
availableConfigurations: [],
availableSyringeTypes: [],
configurationPath: "",
selectedQmixConfig: "",
selectedSyringeType: "",
userEnteredBubbleToggle: "",
Expand Down Expand Up @@ -78,6 +79,10 @@ class PumpForm extends Component {
// --- Update state by Configuration and Syringe Size fields --- //
handleConfigNameChange = (e) => this.setState({selectedQmixConfig: e.target.innerText});
handleSyringeTypeChange = (e) => this.setState({selectedSyringeType: e.target.innerText});
handleConfigPathChange = (e) => {
this.setState({configurationPath: e.target.value});
this.updateAvailableConfigurations(e.target.value);
};
handleLocatingConfig = () => {
this.toggle('locateConfigFiles');
this.setState({userEnteredPumpConfiguration: true})
Expand Down Expand Up @@ -321,9 +326,12 @@ class PumpForm extends Component {
},
});
const json = await response.json();
await this.asyncSetState({isPumpConfigSetUp: json['is_config_set_up']});
// Uncommented since I want to configure pumps every time I press 'Detect Pumps'
// await this.asyncSetState({isPumpConfigSetUp: json['is_config_set_up']});
await this.asyncSetState({isPumpConfigSetUp: false});
await this.asyncSetState({availableConfigurations: json['available_configs']});
await this.asyncSetState({availableSyringeTypes: json['available_syringes']});
await this.asyncSetState({configurationPath: json['configuration_path']});

// Use first item as default.
const defaultConfig = this.state.availableConfigurations[0];
Expand All @@ -332,6 +340,23 @@ class PumpForm extends Component {
await this.asyncSetState({selectedSyringeType: defaultSyringeType})
};

updateAvailableConfigurations = async (configPath) => {
let payload = {configPath: configPath};
const response = await fetch('/api/config_update', {
method: 'put',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
const json = await response.json();
await this.asyncSetState({availableConfigurations: json['available_configs']});

const defaultConfig = this.state.availableConfigurations[0];
await this.asyncSetState({selectedQmixConfig: defaultConfig});
};

// --- Function to wait for user input --- //
waitForConfigFilesToBeSet = async () => {
do {
Expand Down Expand Up @@ -713,26 +738,47 @@ class PumpForm extends Component {
onSubmit={(e) => {
e.preventDefault();
}}>
{/*Dropdown for config name*/}
<FormGroup>
<Dropdown isOpen={this.state.modal['configDropDownOpen']}
toggle={() => this.toggle('configDropDownOpen')}>
<DropdownToggle caret>
{this.state.selectedQmixConfig}
</DropdownToggle>
<DropdownMenu>
{this.state.availableConfigurations.map(config =>
<DropdownItem
key={config}
onClick={this.handleConfigNameChange}>
{config}
</DropdownItem>
)}
</DropdownMenu>
</Dropdown>
</FormGroup>

<form>
<div class="form-row">

<div class="col">
{/*Specify configuration directory*/}
<h6>Configuration directory:</h6>
<FormGroup>
<input type="text"
className="form-control"
onChange={this.handleConfigPathChange}
placeholder={this.state.configurationPath}/>
</FormGroup>
</div>

<div className="col">
{/*Dropdown to choose configuration*/}
<h6>Select configuration:</h6>
<FormGroup>
<Dropdown isOpen={this.state.modal['configDropDownOpen']}
toggle={() => this.toggle('configDropDownOpen')}>
<DropdownToggle caret>
{this.state.selectedQmixConfig}
</DropdownToggle>
<DropdownMenu>
{this.state.availableConfigurations.map(config =>
<DropdownItem
key={config}
onClick={this.handleConfigNameChange}>
{config}
</DropdownItem>
)}
</DropdownMenu>
</Dropdown>
</FormGroup>
</div>
</div>
</form>

<FormGroup>
<h6>Select syringe type:</h6>
<Dropdown isOpen={this.state.modal['syringeDropDownOpen']}
toggle={() => this.toggle('syringeDropDownOpen')}>
<DropdownToggle caret>
Expand All @@ -755,6 +801,7 @@ class PumpForm extends Component {

<ModalFooter>
<Button color="success"
disabled={this.state.availableConfigurations.length === 0}
onClick={this.handleLocatingConfig}> Continue </Button>
<Button color="danger"
onClick={() => this.toggle('locateConfigFiles')}> Cancel </Button>
Expand All @@ -765,8 +812,15 @@ class PumpForm extends Component {
className={this.props.className}>
<ModalHeader >Error - no pumps were detected.</ModalHeader>
<ModalBody>
Check that (1) the pumps are powered on and connected to the computer, and (2) that the bus is not
connected already. Then try again.
Ensure that:
<ul>
<li>You followed the gustometer installation <a href="https://github.com/psyfood/pyqmix#gustometer-setup">instructions.</a></li>
<li>That you selected a valid pump configuration.</li>
<li>That the pumps are powered on and connected to the computer.</li>
<li>That the bus is not in use by another program, for example QmixElements.</li>
<li>If the above approaches are unsuccessful, then try adding the directory of the Qmix SDK DLL's to the Windows path variable.</li>
</ul>
Then try again.
</ModalBody>
<ModalFooter>
<Button color="success"
Expand Down

0 comments on commit 46b729e

Please sign in to comment.