Skip to content

Commit

Permalink
Merge pull request #36 from machineagency/text-workflow
Browse files Browse the repository at this point in the history
Implement sonication protocol actions
  • Loading branch information
branchwelder committed Dec 5, 2020
2 parents 5290434 + 4ea7b62 commit 80c10da
Show file tree
Hide file tree
Showing 11 changed files with 412 additions and 48 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# The Planager

The Planager is a flow-based programming environment that allows you to create workflows that span digital, physical, and human domains.
The Planager is a flow-based, end-user programming environment that allows you to create workflows that span digital, physical, and human domains. Here is an example workflow for scintillation with a Jubilee CNC that is configured for pipetting:

![](docs/evolution/2020_11_17.png)
![](docs/evolution/2020_12_4.png)

## Get started

Ensure you have the latest version of [node](https://nodejs.org/en/) installed before starting. Clone and download this repo. Ensure you're running the latest version of npm: `npm update -g`. In the top-level directory, run `sudo npm install`. This will install all of the dependencies, which are listed in `package.json`. The dependencies are installed to `/node_modules`, which is in the `.gitignore` so it will not be committed to github.
To run the planager, first ensure you have the latest version of [node](https://nodejs.org/en/) installed before starting. Clone and download this repo. Ensure you're running the latest version of npm: `npm update -g`. In the top-level directory, run `sudo npm install`. This will install all of the dependencies, which are listed in `package.json`. The dependencies are installed to `/node_modules`, which is in the `.gitignore` so it will not be committed to github.

## To Run the App

Expand Down
Binary file added docs/evolution/2020_12_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 9 additions & 3 deletions src/components/actions/ActionLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import Alert from "./alert/Alert";
import Constant from "./constant/Constant";
import JubileeDeck from "./jubileedeck/JubileeDeck";
import Wellplate from "./wellplate/Wellplate";
// import TextStep from "./textstep/TextStep";
import BuildSonicationProtocol from "./buildSonicationProtocol/BuildSonicationProtocol";
import TextStep from "./textstep/TextStep";
import ProtocolViewer from "./protocolViewer/ProtocolViewer";
import DownloadFile from "./downloadFile/DownloadFile";
// import FirstStep from "./firststep/FirstStep";
import Zip from "./zip/Zip";
// import Zip from "./zip/Zip";
// import Conditional from "./conditional/Conditional";

// In order for an action to show up on the main page, it must be imported
Expand All @@ -17,7 +20,10 @@ export {
JubileeDeck,
Wellplate,
// TextStep,
BuildSonicationProtocol,
ProtocolViewer,
DownloadFile,
// FirstStep,
Zip,
// Zip,
// Conditional,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React from "react";
import GenericAction from "../GenericAction";
import Inport from "../../base/Inport";
import Outport from "../../base/Outport";
import SonicationProtocol from "./SonicationProtocol";

import "./buildSonicationProtocol.css";
import Jubilee from "../jubileedeck/Jubilee";

export default class BuildSonicationProtocol extends React.Component {
constructor(props) {
super(props);
this.state = {
inports: [
new Inport("Input", Jubilee, new Jubilee(), "The Jubilee Deck"),
],
outports: [
new Outport(
"Output",
SonicationProtocol,
new SonicationProtocol(),
"A sonication protocol."
),
],
};
}

static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.payload) return null;

// This is where data comes in from a link and is assigned to a port
// This is where you should do anything that should happen when a link is connected
prevState.inports[0].data = nextProps.payload.data.data;

return prevState;
}

addSonicationCommand(slot) {
console.log("making a command");
console.log(slot);
}

renderDeck() {
if (!this.state.inports[0].data) return;
let preview = [];
for (var slot = 0; slot < this.state.inports[0].data.deck.length; slot++) {
preview.push(
<div
className="deckSlot"
key={slot}
onClick={this.addSonicationCommand.bind(this, slot)}
>
{!this.state.inports[0].data.deck[slot]
? ""
: `${this.state.inports[0].data.deck[slot].name}`}
<br />
{!this.state.inports[0].data.deck[slot]
? `empty`
: `${this.state.inports[0].data.deck[slot].xWells}x${this.state.inports[0].data.deck[slot].yWells}`}
</div>
);
}

preview.splice(2, 0, <br key={`break1`} />);
preview.splice(5, 0, <br key={`break2`} />);

return preview;
}

addStep() {
let outports = [...this.state.outports];
outports[0].data.addStep(1, "A", 2, 3.0, 2, true);
this.setState({ outports: outports });
}

renderProtocol() {
if (!this.state.outports[0].data) return;
let protocolView = [];
const protocol = this.state.outports[0].data.protocol.protocol;
for (var step = 0; step < protocol.length; step++) {
protocolView.push(
<div key={step}>
<span>{`${step}. Sonicate well ${protocol[step].specs.row_letter}${
step + 1
}\n`}</span>
</div>
);
}
return protocolView;
}

render() {
return (
<GenericAction
inports={this.state.inports}
outports={this.state.outports}
actionID={this.props.id}
>
<div className="actionTitle">Create Sonication Protocol</div>
<div className="actionContent">
<div className="deck">{this.renderDeck.bind(this)()}</div>
<div>
<button
className="planagerButton"
onClick={this.addStep.bind(this)}
>
Add Step
</button>
</div>
<div className="protocol">{this.renderProtocol.bind(this)()}</div>
</div>
</GenericAction>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable no-throw-literal */

export default class SonicationProtocol {
constructor() {
this._protocol = { protocol: [] };
}

toJSON() {
return `Sonication Protocol object`;
}

addStep(
deck_index,
row_letter,
column_index,
plunge_depth,
seconds,
autoclean = true
) {
this._protocol.protocol.push({
operation: "sonicate_well",
specs: {
deck_index: deck_index,
row_letter: row_letter,
column_index: column_index,
plunge_depth: plunge_depth,
seconds: seconds,
autoclean: autoclean,
},
});
}

removeStep(index) {
this._protocol.splice(index, 1);
}

/**
* @returns {Object}
*/
get protocol() {
return this._protocol;
}

// /**
// * @param {Object} Jubilee Deck
// */
set deck(jubileeDeck) {
this._deck = jubileeDeck;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.deckSlot {
height: 50px;
width: 100px;
padding: 7px;
border-radius: 4px;
display: inline-block;
margin: 3px;
border: 1px solid gray;
cursor: pointer;
user-select: none;
}

.deck {
padding: 5px;
border: 1px solid gray;
margin: 4px;
border-radius: 5px;
background-color: #f7f7f7;
width: max-content;
}
53 changes: 53 additions & 0 deletions src/components/actions/downloadFile/DownloadFile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react";
import GenericAction from "../GenericAction";
import Inport from "../../base/Inport";
import Outport from "../../base/Outport";

export default class DownloadFile extends React.Component {
constructor(props) {
super(props);
this.state = {
inports: [
new Inport("Input", "any", null, "The data to be displayed.")
],
outports: [
new Outport("Output", "any", null, "The data that was displayed."),
],
};
}

static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.payload) return null;

// This is where data comes in from a link and is assigned to a port
// This is where you should do anything that should happen when a link is connected
prevState.inports[0].data = nextProps.payload.data.data;
prevState.outports[0].data = nextProps.payload.data.data;

return prevState;
}

download() {
alert(`Value: ${JSON.stringify(this.state.inports[0].data.protocol)}`);
}

render() {
return (
<GenericAction
inports={this.state.inports}
outports={this.state.outports}
actionID={this.props.id}
>
<div className="actionTitle">Download</div>
<div className="actionContent">
<input
type="button"
value="Download"
className="planagerButton"
onClick={this.download.bind(this)}
/>
</div>
</GenericAction>
);
}
}
8 changes: 4 additions & 4 deletions src/components/actions/jubileedeck/JubileeDeck.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ export default class JubileeDeck extends React.Component {
super(props);
this.state = {
inports: [
new Inport("Slot 1", Plate, null, "Jubilee deck slot one."),
new Inport("Slot 2", Plate, null, "Jubilee deck slot two."),
new Inport("Slot 3", Plate, null, "Jubilee deck slot three."),
new Inport("Slot 4", Plate, null, "Jubilee deck slot four."),
new Inport("Slot 5", Plate, null, "Jubilee deck slot five."),
new Inport("Slot 6", Plate, null, "Jubilee deck slot six."),
new Inport("Slot 2", Plate, null, "Jubilee deck slot two."),
new Inport("Slot 3", Plate, null, "Jubilee deck slot three."),
new Inport("Slot 0", Plate, null, "Jubilee deck slot zero."),
new Inport("Slot 1", Plate, null, "Jubilee deck slot one."),
],
outports: [
new Outport(
Expand Down
55 changes: 55 additions & 0 deletions src/components/actions/protocolViewer/ProtocolViewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from "react";
import GenericAction from "../GenericAction";
import Inport from "../../base/Inport";
import Outport from "../../base/Outport";

export default class ProtocolViewer extends React.Component {
constructor(props) {
super(props);
this.state = {
inports: [new Inport("Input", "any", null, "The data to be displayed.")],
outports: [
new Outport("Output", "any", null, "The data that was displayed."),
],
};
}

static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.payload) return null;

// This is where data comes in from a link and is assigned to a port
// This is where you should do anything that should happen when a link is connected
prevState.inports[0].data = nextProps.payload.data.data;
prevState.outports[0].data = nextProps.payload.data.data;

return prevState;
}

formatSteps() {
let steps = [];
const protocol = this.state.inports[0].data.protocol.protocol;
for (let step = 0; step < protocol.length; step++) {
steps.push(
<div key={step}>
<span>{`${step}. Sonicate well ${protocol[step].specs.row_letter}${step+1}\n`}</span>
</div>
);
}
return steps;
}

render() {
return (
<GenericAction
inports={this.state.inports}
outports={this.state.outports}
actionID={this.props.id}
>
<div className="actionTitle">Protocol Viewer</div>
<div className="actionContent">
{this.state.inports[0].data ? this.formatSteps.bind(this)() : "Empty"}
</div>
</GenericAction>
);
}
}
Loading

0 comments on commit 80c10da

Please sign in to comment.