update tutorial structure

This commit is contained in:
Mario 2020-10-08 15:29:28 +02:00
parent aaeaf7a5aa
commit 89d0cc380b
19 changed files with 420 additions and 411 deletions

View File

@ -1,6 +1,6 @@
import { TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE, TUTORIAL_XML, TUTORIAL_ID, TUTORIAL_STEP } from './types';
import tutorials from '../data/tutorials.json';
import tutorials from '../data/tutorials';
export const tutorialChange = () => (dispatch) => {
dispatch({
@ -26,10 +26,10 @@ export const tutorialCheck = (status, step) => (dispatch, getState) => {
export const storeTutorialXml = (code) => (dispatch, getState) => {
var id = getState().tutorial.currentId;
if(id !== null){
if (id !== null) {
var activeStep = getState().tutorial.activeStep;
var steps = tutorials.filter(tutorial => tutorial.id === id)[0].steps;
if(steps[activeStep].type === 'task'){
if (steps[activeStep].type === 'task') {
var tutorialsStatus = getState().tutorial.status;
var tutorialsStatusIndex = tutorialsStatus.findIndex(tutorialStatus => tutorialStatus.id === id);
var tasksIndex = tutorialsStatus[tutorialsStatusIndex].tasks.findIndex(task => task.id === steps[activeStep].id);

View File

@ -42,7 +42,7 @@ export const tutorialId = (id) => (dispatch) => {
export const addStep = (index) => (dispatch, getState) => {
var steps = getState().builder.steps;
var step = {
id: index+1,
id: index + 1,
type: 'instruction',
headline: '',
text: ''
@ -88,11 +88,11 @@ export const removeErrorStep = (index) => (dispatch, getState) => {
export const changeContent = (content, index, property1, property2) => (dispatch, getState) => {
var steps = getState().builder.steps;
var step = steps[index];
if(property2){
if(step[property1] && step[property1][property2]){
if (property2) {
if (step[property1] && step[property1][property2]) {
step[property1][property2] = content;
} else {
step[property1] = {[property2]: content};
step[property1] = { [property2]: content };
}
} else {
step[property1] = content;
@ -107,8 +107,8 @@ export const changeContent = (content, index, property1, property2) => (dispatch
export const deleteProperty = (index, property1, property2) => (dispatch, getState) => {
var steps = getState().builder.steps;
var step = steps[index];
if(property2){
if(step[property1] && step[property1][property2]){
if (property2) {
if (step[property1] && step[property1][property2]) {
delete step[property1][property2];
}
} else {
@ -147,7 +147,7 @@ export const changeErrorStepIndex = (fromIndex, toIndex) => (dispatch, getState)
export const setError = (index, property) => (dispatch, getState) => {
var error = getState().builder.error;
if(index !== undefined){
if (index !== undefined) {
error.steps[index][property] = true;
}
else {
@ -162,7 +162,7 @@ export const setError = (index, property) => (dispatch, getState) => {
export const deleteError = (index, property) => (dispatch, getState) => {
var error = getState().builder.error;
if(index !== undefined){
if (index !== undefined) {
delete error.steps[index][property];
}
else {
@ -177,44 +177,44 @@ export const deleteError = (index, property) => (dispatch, getState) => {
export const setSubmitError = () => (dispatch, getState) => {
var builder = getState().builder;
if(builder.id === undefined || builder.id === ''){
dispatch(setError(undefined, 'id'));
}
if(builder.id === undefined || builder.title === ''){
// if(builder.id === undefined || builder.id === ''){
// dispatch(setError(undefined, 'id'));
// }
if (builder.id === undefined || builder.title === '') {
dispatch(setError(undefined, 'title'));
}
var type = builder.steps.map((step, i) => {
// media and xml are directly checked for errors in their components and
// therefore do not have to be checked again
step.id = i+1;
if(i === 0){
if(step.requirements && step.requirements.length > 0){
var requirements = step.requirements.filter(requirement => typeof(requirement)==='number');
if(requirements.length < step.requirements.length){
step.id = i + 1;
if (i === 0) {
if (step.requirements && step.requirements.length > 0) {
var requirements = step.requirements.filter(requirement => typeof (requirement) === 'number');
if (requirements.length < step.requirements.length) {
dispatch(changeContent(requirements, i, 'requirements'));
}
}
if(step.hardware === undefined || step.hardware.length < 1){
if (step.hardware === undefined || step.hardware.length < 1) {
dispatch(setError(i, 'hardware'));
}
else{
else {
var hardwareIds = data.map(hardware => hardware.id);
var hardware = step.hardware.filter(hardware => hardwareIds.includes(hardware));
if(hardware.length < step.hardware.length){
if (hardware.length < step.hardware.length) {
dispatch(changeContent(hardware, i, 'hardware'));
}
}
}
if(step.headline === undefined || step.headline === ''){
if (step.headline === undefined || step.headline === '') {
dispatch(setError(i, 'headline'));
}
if(step.text === undefined || step.text === ''){
if (step.text === undefined || step.text === '') {
dispatch(setError(i, 'text'));
}
return step.type;
});
if(!(type.filter(item => item === 'task').length > 0 && type.filter(item => item === 'instruction').length > 0)){
dispatch(setError(undefined, 'type'));
if (!(type.filter(item => item === 'task').length > 0 && type.filter(item => item === 'instruction').length > 0)) {
dispatch(setError(undefined, 'type'));
}
};
@ -222,11 +222,11 @@ export const setSubmitError = () => (dispatch, getState) => {
export const checkError = () => (dispatch, getState) => {
dispatch(setSubmitError());
var error = getState().builder.error;
if(error.id || error.title || error.type){
if (error.id || error.title || error.type) {
return true;
}
for(var i = 0; i < error.steps.length; i++){
if(Object.keys(error.steps[i]).length > 0){
for (var i = 0; i < error.steps.length; i++) {
if (Object.keys(error.steps[i]).length > 0) {
return true
}
}
@ -268,7 +268,7 @@ export const readJSON = (json) => (dispatch, getState) => {
dispatch({
type: BUILDER_ERROR,
payload: {
steps: json.steps.map(() => {return {};})
steps: json.steps.map(() => { return {}; })
}
});
// accept only valid attributes
@ -279,19 +279,19 @@ export const readJSON = (json) => (dispatch, getState) => {
headline: step.headline,
text: step.text
};
if(i === 0){
if (i === 0) {
object.hardware = step.hardware;
object.requirements = step.requirements;
}
if(step.xml){
if (step.xml) {
object.xml = step.xml;
}
if(step.media && step.type === 'instruction'){
if (step.media && step.type === 'instruction') {
object.media = {};
if(step.media.picture){
if (step.media.picture) {
object.media.picture = step.media.picture;
}
else if(step.media.youtube){
else if (step.media.youtube) {
object.media.youtube = step.media.youtube;
}
}

View File

@ -18,6 +18,7 @@ export const CHARACTER = {
export const BOOLEAN = {
typeId: 'Boolean',
typeName: 'Boolean',
typeMsgName: 'ARD_TYPE_BOOL',
}
@ -89,7 +90,7 @@ const compatibleTypes = {
boolean: ['boolean'],
int: ['int'],
char: ['char'],
string: ['String'],
String: ['String'],
void: ['void'],
long: ['int', 'long'],
double: ['int', 'long', 'double'],

View File

@ -33,7 +33,7 @@ const styles = (theme) => ({
class Builder extends Component {
constructor(props){
constructor(props) {
super(props);
this.state = {
open: false,
@ -47,26 +47,33 @@ class Builder extends Component {
this.inputRef = React.createRef();
}
componentWillUnmount(){
componentWillUnmount() {
this.reset();
}
submit = () => {
console.log(this.props.id)
if (this.props.id === null) {
var randomID = Date.now();
} else {
randomID = this.props.id;
}
var isError = this.props.checkError();
if(isError){
this.setState({ snackbar: true, key: Date.now(), message: `Die Angaben für das Tutorial sind nicht vollständig.`, type: 'error'});
if (isError) {
this.setState({ snackbar: true, key: Date.now(), message: `Die Angaben für das Tutorial sind nicht vollständig.`, type: 'error' });
window.scrollTo(0, 0);
}
else{
else {
// export steps without attribute 'url'
var steps = this.props.steps.map(step => {
if(step.url){
if (step.url) {
delete step.url;
}
return step;
});
var tutorial = {
id: this.props.id,
id: randomID,
title: this.props.title,
steps: steps
}
@ -77,15 +84,15 @@ class Builder extends Component {
reset = () => {
this.props.resetTutorial();
this.setState({ snackbar: true, key: Date.now(), message: `Das Tutorial wurde erfolgreich zurückgesetzt.`, type: 'success'});
this.setState({ snackbar: true, key: Date.now(), message: `Das Tutorial wurde erfolgreich zurückgesetzt.`, type: 'success' });
window.scrollTo(0, 0);
}
uploadJsonFile = (jsonFile) => {
this.props.progress(true);
if(jsonFile.type !== 'application/json'){
if (jsonFile.type !== 'application/json') {
this.props.progress(false);
this.setState({ open: true, string: false, title: 'Unzulässiger Dateityp', content: 'Die übergebene Datei entspricht nicht dem geforderten Format. Es sind nur JSON-Dateien zulässig.'});
this.setState({ open: true, string: false, title: 'Unzulässiger Dateityp', content: 'Die übergebene Datei entspricht nicht dem geforderten Format. Es sind nur JSON-Dateien zulässig.' });
}
else {
var reader = new FileReader();
@ -97,27 +104,27 @@ class Builder extends Component {
}
uploadJsonString = () => {
this.setState({ open: true, string: true, title: 'JSON-String einfügen', content: ''});
this.setState({ open: true, string: true, title: 'JSON-String einfügen', content: '' });
}
readJson = (jsonString, isFile) => {
try {
var result = JSON.parse(jsonString);
if(!this.checkSteps(result.steps)){
if (!this.checkSteps(result.steps)) {
result.steps = [{}];
}
this.props.readJSON(result);
this.setState({ snackbar: true, key: Date.now(), message: `${isFile ? 'Die übergebene JSON-Datei' : 'Der übergebene JSON-String'} wurde erfolgreich übernommen.`, type: 'success'});
} catch(err){
this.setState({ snackbar: true, key: Date.now(), message: `${isFile ? 'Die übergebene JSON-Datei' : 'Der übergebene JSON-String'} wurde erfolgreich übernommen.`, type: 'success' });
} catch (err) {
console.log(err);
this.props.progress(false);
this.props.jsonString('');
this.setState({ open: true, string: false, title: 'Ungültiges JSON-Format', content: `${isFile ? 'Die übergebene Datei' : 'Der übergebene String'} enthält nicht valides JSON. Bitte überprüfe ${isFile ? 'die JSON-Datei' : 'den JSON-String'} und versuche es erneut.`});
this.setState({ open: true, string: false, title: 'Ungültiges JSON-Format', content: `${isFile ? 'Die übergebene Datei' : 'Der übergebene String'} enthält nicht valides JSON. Bitte überprüfe ${isFile ? 'die JSON-Datei' : 'den JSON-String'} und versuche es erneut.` });
}
}
checkSteps = (steps) => {
if(!(steps && steps.length > 0)){
if (!(steps && steps.length > 0)) {
return false;
}
return true;
@ -131,41 +138,41 @@ class Builder extends Component {
render() {
return (
<div>
<Breadcrumbs content={[{link: '/tutorial', title: 'Tutorial'}, {link: '/tutorial/builder', title: 'Builder'}]}/>
<Breadcrumbs content={[{ link: '/tutorial', title: 'Tutorial' }, { link: '/tutorial/builder', title: 'Builder' }]} />
<h1>Tutorial-Builder</h1>
{/*upload JSON*/}
<div ref={this.inputRef}>
<input
style={{display: 'none'}}
style={{ display: 'none' }}
accept="application/json"
onChange={(e) => {this.uploadJsonFile(e.target.files[0])}}
onChange={(e) => { this.uploadJsonFile(e.target.files[0]) }}
id="open-json"
type="file"
/>
<label htmlFor="open-json">
<Button component="span" style={{marginRight: '10px', marginBottom: '10px'}} variant='contained' color='primary'>Datei laden</Button>
<Button component="span" style={{ marginRight: '10px', marginBottom: '10px' }} variant='contained' color='primary'>Datei laden</Button>
</label>
<Button style={{marginRight: '10px', marginBottom: '10px'}} variant='contained' color='primary' onClick={() => this.uploadJsonString()}>String laden</Button>
<Button style={{ marginRight: '10px', marginBottom: '10px' }} variant='contained' color='primary' onClick={() => this.uploadJsonString()}>String laden</Button>
</div>
<Divider variant='fullWidth' style={{margin: '10px 0 15px 0'}}/>
<Divider variant='fullWidth' style={{ margin: '10px 0 15px 0' }} />
{/*Tutorial-Builder-Form*/}
{this.props.error.type ?
<FormHelperText style={{lineHeight: 'initial'}} className={this.props.classes.errorColor}>{`Ein Tutorial muss mindestens jeweils eine Instruktion und eine Aufgabe enthalten.`}</FormHelperText>
: null}
<Id error={this.props.error.id} value={this.props.id}/>
<Textfield value={this.props.title} property={'title'} label={'Titel'} error={this.props.error.title}/>
<FormHelperText style={{ lineHeight: 'initial' }} className={this.props.classes.errorColor}>{`Ein Tutorial muss mindestens jeweils eine Instruktion und eine Aufgabe enthalten.`}</FormHelperText>
: null}
{/* <Id error={this.props.error.id} value={this.props.id} /> */}
<Textfield value={this.props.title} property={'title'} label={'Titel'} error={this.props.error.title} />
{this.props.steps.map((step, i) =>
<Step step={step} index={i} key={i}/>
<Step step={step} index={i} key={i} />
)}
{/*submit or reset*/}
<Divider variant='fullWidth' style={{margin: '30px 0 10px 0'}}/>
<Button style={{marginRight: '10px', marginTop: '10px'}} variant='contained' color='primary' onClick={() => this.submit()}>Tutorial-Vorlage erstellen</Button>
<Button style={{marginTop: '10px'}} variant='contained' onClick={() => this.reset()}>Zurücksetzen</Button>
<Divider variant='fullWidth' style={{ margin: '30px 0 10px 0' }} />
<Button style={{ marginRight: '10px', marginTop: '10px' }} variant='contained' color='primary' onClick={() => this.submit()}>Tutorial-Vorlage erstellen</Button>
<Button style={{ marginTop: '10px' }} variant='contained' onClick={() => this.reset()}>Zurücksetzen</Button>
<Backdrop className={this.props.classes.backdrop} open={this.props.isProgress}>
<CircularProgress color="inherit" />
@ -182,16 +189,16 @@ class Builder extends Component {
button={'Schließen'}
actions={
this.state.string ?
<div>
<Button disabled={this.props.error.json || this.props.json === ''} variant='contained' onClick={() => {this.toggle(); this.props.progress(true); this.readJson(this.props.json, false);}} color="primary">Bestätigen</Button>
<Button onClick={() => {this.toggle(); this.props.jsonString('');}} color="primary">Abbrechen</Button>
</div>
: null
<div>
<Button disabled={this.props.error.json || this.props.json === ''} variant='contained' onClick={() => { this.toggle(); this.props.progress(true); this.readJson(this.props.json, false); }} color="primary">Bestätigen</Button>
<Button onClick={() => { this.toggle(); this.props.jsonString(''); }} color="primary">Abbrechen</Button>
</div>
: null
}
>
{this.state.string ?
<Textfield value={this.props.json} property={'json'} label={'JSON'} multiline error={this.props.error.json}/>
: null}
<Textfield value={this.props.json} property={'json'} label={'JSON'} multiline error={this.props.error.json} />
: null}
</Dialog>
<Snackbar
@ -230,4 +237,4 @@ const mapStateToProps = state => ({
isProgress: state.builder.progress
});
export default connect(mapStateToProps, { checkError, readJSON, jsonString, progress, resetTutorial })(withStyles(styles, {withTheme: true})(Builder));
export default connect(mapStateToProps, { checkError, readJSON, jsonString, progress, resetTutorial })(withStyles(styles, { withTheme: true })(Builder));

View File

@ -28,81 +28,83 @@ const styles = theme => ({
class Id extends Component {
handleChange = (e) => {
var value = parseInt(e.target.value);
if(Number.isInteger(value) && value > 0){
var value =
parseInt(e.target.value);
if (Number.isInteger(value) && value > 0) {
this.props.tutorialId(value);
if(this.props.error){
if (this.props.error) {
this.props.deleteError(undefined, 'id');
}
}
else {
this.props.tutorialId(value.toString());
this.props.setError(undefined,'id');
this.props.setError(undefined, 'id');
}
};
handleCounter = (step) => {
if(this.props.value+step < 1){
this.props.setError(undefined,'id');
if (this.props.value + step < 1) {
this.props.setError(undefined, 'id');
}
else if(this.props.error){
else if (this.props.error) {
this.props.deleteError(undefined, 'id');
}
if(!this.props.value || !Number.isInteger(this.props.value)){
this.props.tutorialId(0+step);
if (!this.props.value || !Number.isInteger(this.props.value)) {
this.props.tutorialId(0 + step);
}
else {
this.props.tutorialId(this.props.value+step);
this.props.tutorialId(this.props.value + step);
}
}
render() {
return (
<div style={{display: 'inline-flex', marginTop: '15px'}}>
<FormControl variant="outlined" style={{marginBottom: '10px', width: '250px'}}>
<InputLabel
htmlFor="id"
classes={{shrink: this.props.error ? this.props.classes.errorColorShrink : null}}
>
ID
<div style={{ display: 'inline-flex', marginTop: '15px' }}>
<FormControl variant="outlined" style={{ marginBottom: '10px', width: '250px' }}>
<InputLabel
htmlFor="id"
classes={{ shrink: this.props.error ? this.props.classes.errorColorShrink : null }}
>
ID
</InputLabel>
<OutlinedInput
style={{borderRadius: '25px', padding: '0 0 0 10px', width: '200px'}}
classes={{notchedOutline: this.props.error ? this.props.classes.errorBorder : null}}
error={this.props.error}
value={this.props.value}
name='id'
label='ID'
id='id'
onChange={this.handleChange}
inputProps={{
style: {marginRight: '10px'}
}}
endAdornment={
<div style={{display: 'flex'}}>
<Button
disabled={this.props.value === 1 || !Number.isInteger(this.props.value)}
onClick={() => this.handleCounter(-1)}
variant='contained'
color='primary'
style={{borderRadius: '25px 0 0 25px', height: '56px', boxShadow: '0 0 transparent'}}
>
<FontAwesomeIcon icon={faMinus} />
</Button>
<Button
onClick={() => this.handleCounter(1)}
variant='contained'
color='primary'
style={{borderRadius: '0 25px 25px 0', height: '56px', boxShadow: '0 0 transparent'}}
>
<FontAwesomeIcon icon={faPlus} />
</Button>
</div>
}
/>
{this.props.error ? <FormHelperText className={this.props.classes.errorColor}>Gib eine positive ganzzahlige Zahl ein.</FormHelperText> : null}
</FormControl>
<FormHelperText style={{marginLeft: '-40px', marginTop: '5px', lineHeight: 'initial', marginBottom: '10px', width: '200px'}}>Beachte, dass die ID eindeutig sein muss. Sie muss sich also zu den anderen Tutorials unterscheiden.</FormHelperText>
<OutlinedInput
style={{ borderRadius: '25px', padding: '0 0 0 10px', width: '200px' }}
classes={{ notchedOutline: this.props.error ? this.props.classes.errorBorder : null }}
error={this.props.error}
value={this.props.value}
name='id'
label='ID'
id='id'
onChange={this.handleChange}
inputProps={{
style: { marginRight: '10px' }
}}
endAdornment={
<div style={{ display: 'flex' }}>
<Button
disabled={this.props.value === 1 || !Number.isInteger(this.props.value)}
onClick={() => this.handleCounter(-1)}
variant='contained'
color='primary'
style={{ borderRadius: '25px 0 0 25px', height: '56px', boxShadow: '0 0 transparent' }}
>
<FontAwesomeIcon icon={faMinus} />
</Button>
<Button
onClick={() => this.handleCounter(1)}
variant='contained'
color='primary'
style={{ borderRadius: '0 25px 25px 0', height: '56px', boxShadow: '0 0 transparent' }}
>
<FontAwesomeIcon icon={faPlus} />
</Button>
</div>
}
/>
{this.props.error ? <FormHelperText className={this.props.classes.errorColor}>Gib eine positive ganzzahlige Zahl ein.</FormHelperText> : null}
</FormControl>
<FormHelperText style={{ marginLeft: '-40px', marginTop: '5px', lineHeight: 'initial', marginBottom: '10px', width: '200px' }}>Beachte, dass die ID eindeutig sein muss. Sie muss sich also zu den anderen Tutorials unterscheiden.</FormHelperText>
</div>
);
};

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { changeContent } from '../../../actions/tutorialBuilderActions';
import tutorials from '../../../data/tutorials.json';
import tutorials from '../../../data/tutorials';
import FormGroup from '@material-ui/core/FormGroup';
import Checkbox from '@material-ui/core/Checkbox';
@ -17,7 +17,7 @@ class Requirements extends Component {
onChange = (e) => {
var requirements = this.props.value;
var value = parseInt(e.target.value)
if(e.target.checked){
if (e.target.checked) {
requirements.push(value);
}
else {
@ -28,9 +28,9 @@ class Requirements extends Component {
render() {
return (
<FormControl style={{marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)'}}>
<FormLabel style={{color: 'black'}}>Voraussetzungen</FormLabel>
<FormHelperText style={{marginTop: '5px'}}>Beachte, dass die Reihenfolge des Anhakens maßgebend ist.</FormHelperText>
<FormControl style={{ marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)' }}>
<FormLabel style={{ color: 'black' }}>Voraussetzungen</FormLabel>
<FormHelperText style={{ marginTop: '5px' }}>Beachte, dass die Reihenfolge des Anhakens maßgebend ist.</FormHelperText>
<FormGroup>
{tutorials.map((tutorial, i) =>
<FormControlLabel

View File

@ -28,7 +28,8 @@ const styles = theme => ({
color: theme.palette.text.primary
},
multiGridListTile: {
background: fade(theme.palette.secondary.main, 0.5),
background: "#4EAF47",
opacity: 0.9,
height: '30px'
},
multiGridListTileTitle: {
@ -45,46 +46,47 @@ class Hardware extends Component {
};
handleClickOpen = (hardwareInfo) => {
this.setState({open: true, hardwareInfo});
this.setState({ open: true, hardwareInfo });
};
handleClose = () => {
this.setState({open: false, hardwareInfo: {}});
this.setState({ open: false, hardwareInfo: {} });
};
render() {
var cols = isWidthDown('md', this.props.width) ? isWidthDown('sm', this.props.width) ? isWidthDown('xs', this.props.width) ? 2 : 3 : 4 : 6;
return (
<div style={{marginTop: '10px', marginBottom: '5px'}}>
<div style={{ marginTop: '10px', marginBottom: '5px' }}>
<Typography>Für die Umsetzung benötigst du folgende Hardware:</Typography>
<GridList cellHeight={100} cols={cols} spacing={10}>
{this.props.picture.map((picture,i) => {
var hardwareInfo = hardware.filter(hardware => hardware.id === picture)[0];
return(
<GridListTile key={i}>
<div style={{margin: 'auto', width: 'max-content'}}>
<img src={`/media/hardware/${hardwareInfo.src}`} alt={hardwareInfo.name} height={100} style={{cursor: 'pointer'}} onClick={() => this.handleClickOpen(hardwareInfo)}/>
</div>
<GridListTileBar
classes={{root: this.props.classes.multiGridListTile}}
title={
<div style={{overflow: 'hidden', textOverflow: 'ellipsis'}} className={this.props.classes.multiGridListTileTitle}>
{hardwareInfo.name}
</div>
}
actionIcon={
<IconButton className={this.props.classes.expand} aria-label='Vollbild' onClick={() => this.handleClickOpen(hardwareInfo)}>
<FontAwesomeIcon icon={faExpandAlt} size="xs"/>
</IconButton>
}
/>
</GridListTile>
)})}
{this.props.picture.map((picture, i) => {
var hardwareInfo = hardware.filter(hardware => hardware.id === picture)[0];
return (
<GridListTile key={i}>
<div style={{ margin: 'auto', width: 'max-content' }}>
<img src={`/media/hardware/${hardwareInfo.src}`} alt={hardwareInfo.name} height={100} style={{ cursor: 'pointer' }} onClick={() => this.handleClickOpen(hardwareInfo)} />
</div>
<GridListTileBar
classes={{ root: this.props.classes.multiGridListTile }}
title={
<div style={{ overflow: 'hidden', textOverflow: 'ellipsis' }} className={this.props.classes.multiGridListTileTitle}>
{hardwareInfo.name}
</div>
}
actionIcon={
<IconButton className={this.props.classes.expand} aria-label='Vollbild' onClick={() => this.handleClickOpen(hardwareInfo)}>
<FontAwesomeIcon icon={faExpandAlt} size="xs" />
</IconButton>
}
/>
</GridListTile>
)
})}
</GridList>
<Dialog
style={{zIndex: 1500}}
style={{ zIndex: 1500 }}
open={this.state.open}
title={`Hardware: ${this.state.hardwareInfo.name}`}
content={this.state.content}
@ -93,7 +95,7 @@ class Hardware extends Component {
button={'Schließen'}
>
<div>
<img src={`/media/hardware/${this.state.hardwareInfo.src}`} width="100%" alt={this.state.hardwareInfo.name}/>
<img src={`/media/hardware/${this.state.hardwareInfo.src}`} width="100%" alt={this.state.hardwareInfo.name} />
Weitere Informationen zur Hardware-Komponente findest du <Link href={this.state.hardwareInfo.url} color="primary">hier</Link>.
</div>
</Dialog>

View File

@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import clsx from 'clsx';
import { withRouter, Link } from 'react-router-dom';
import tutorials from '../../data/tutorials.json';
import tutorials from '../../data/tutorials';
import { fade } from '@material-ui/core/styles/colorManipulator';
import { withStyles } from '@material-ui/core/styles';
@ -63,46 +63,47 @@ class Requirement extends Component {
render() {
var tutorialIds = this.props.tutorialIds;
return (
<div style={{marginTop: '20px', marginBottom: '5px'}}>
<Typography>Es bietet sich an folgende Tutorials vorab erfolgreich gelöst zu haben:</Typography>
<div style={{ marginTop: '20px', marginBottom: '5px' }}>
<Typography>Bevor du mit diesem Tutorial fortfährst solltest du folgende Tutorials erfolgreich abgeschlossen haben:</Typography>
<List component="div">
{tutorialIds.map((tutorialId, i) => {
var title = tutorials.filter(tutorial => tutorial.id === tutorialId)[0].title;
var status = this.props.status.filter(status => status.id === tutorialId)[0];
var tasks = status.tasks;
var error = status.tasks.filter(task => task.type === 'error').length > 0;
var success = status.tasks.filter(task => task.type === 'success').length/tasks.length
var success = status.tasks.filter(task => task.type === 'success').length / tasks.length
var tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other';
return(
return (
<Link to={`/tutorial/${tutorialId}`} className={this.props.classes.link}>
<Tooltip style={{height: '50px', width: '50px', position: 'absolute', background: 'white', zIndex: 1, borderRadius: '25px'}} title={error ? `Mind. eine Aufgabe im Tutorial wurde nicht richtig gelöst.` : success === 1 ? `Das Tutorial wurde bereits erfolgreich abgeschlossen.` : `Das Tutorial ist zu ${success * 100}% abgeschlossen.`} arrow>
<Tooltip style={{ height: '50px', width: '50px', position: 'absolute', background: 'white', zIndex: 1, borderRadius: '25px' }} title={error ? `Mind. eine Aufgabe im Tutorial wurde nicht richtig gelöst.` : success === 1 ? `Das Tutorial wurde bereits erfolgreich abgeschlossen.` : `Das Tutorial ist zu ${success * 100}% abgeschlossen.`} arrow>
<div>
<div className={clsx(this.props.classes.outerDiv)} style={{width: '50px', height: '50px', border: 0}}>
<svg style={{width: '100%', height: '100%'}}>
<div className={clsx(this.props.classes.outerDiv)} style={{ width: '50px', height: '50px', border: 0 }}>
<svg style={{ width: '100%', height: '100%' }}>
{error || success === 1 ?
<circle className={error ? this.props.classes.outerDivError : this.props.classes.outerDivSuccess} r="22.5" cx="50%" cy="50%" fill="none" stroke-width="5"></circle>
: <circle className={this.props.classes.outerDivOther} r="22.5" cx="50%" cy="50%" fill="none" stroke-width="5"></circle>}
: <circle className={this.props.classes.outerDivOther} r="22.5" cx="50%" cy="50%" fill="none" stroke-width="5"></circle>}
{success < 1 && !error ?
<circle className={this.props.classes.outerDivSuccess} style={{transform: 'rotate(-90deg)', transformOrigin: "50% 50%"}} r="22.5" cx="50%" cy="50%" fill="none" stroke-width="5" stroke-dashoffset={`${(22.5*2*Math.PI)*(1-success)}`} stroke-dasharray={`${(22.5*2*Math.PI)}`}>
<circle className={this.props.classes.outerDivSuccess} style={{ transform: 'rotate(-90deg)', transformOrigin: "50% 50%" }} r="22.5" cx="50%" cy="50%" fill="none" stroke-width="5" stroke-dashoffset={`${(22.5 * 2 * Math.PI) * (1 - success)}`} stroke-dasharray={`${(22.5 * 2 * Math.PI)}`}>
</circle>
: null}
: null}
</svg>
</div>
<div className={clsx(this.props.classes.outerDiv, tutorialStatus === 'Error' ? this.props.classes.outerDivError : tutorialStatus === 'Success' ? this.props.classes.outerDivSuccess : null)}>
<div className={this.props.classes.innerDiv}>
{error || success === 1 ?
<FontAwesomeIcon icon={tutorialStatus === 'Success' ? faCheck : faTimes}/>
: <Typography variant='h7' className={success > 0 ? this.props.classes.outerDivSuccess : {}}>{Math.round(success*100)}%</Typography>
<FontAwesomeIcon icon={tutorialStatus === 'Success' ? faCheck : faTimes} />
: <Typography variant='h7' className={success > 0 ? this.props.classes.outerDivSuccess : {}}>{Math.round(success * 100)}%</Typography>
}
</div>
</div>
</div>
</Tooltip>
<div style={{height: '50px', width: 'calc(100% - 25px)', transform: 'translate(25px)'}} className={this.props.classes.hoverLink}>
<Typography style={{margin: 0, position: 'absolute', top: '50%', transform: 'translate(45px, -50%)', maxHeight: '50px', overflow: 'hidden', maxWidth: 'calc(100% - 45px)'/*, textOverflow: 'ellipsis', whiteSpace: 'pre-line', overflowWrap: 'anywhere'*/}}>{title}</Typography>
<div style={{ height: '50px', width: 'calc(100% - 25px)', transform: 'translate(25px)' }} className={this.props.classes.hoverLink}>
<Typography style={{ margin: 0, position: 'absolute', top: '50%', transform: 'translate(45px, -50%)', maxHeight: '50px', overflow: 'hidden', maxWidth: 'calc(100% - 45px)'/*, textOverflow: 'ellipsis', whiteSpace: 'pre-line', overflowWrap: 'anywhere'*/ }}>{title}</Typography>
</div>
</Link>
)}
)
}
)}
</List>
</div>
@ -120,4 +121,4 @@ const mapStateToProps = state => ({
status: state.tutorial.status
});
export default connect(mapStateToProps, null)(withStyles(styles, {withTheme: true})(withRouter(Requirement)));
export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(withRouter(Requirement)));

View File

@ -8,7 +8,7 @@ import { withRouter } from 'react-router-dom';
import Compile from '../Compile';
import Dialog from '../Dialog';
import tutorials from '../../data/tutorials.json';
import tutorials from '../../data/tutorials';
import { checkXml } from '../../helpers/compareXml';
import { withStyles } from '@material-ui/core/styles';
@ -32,16 +32,16 @@ const styles = (theme) => ({
class SolutionCheck extends Component {
state={
state = {
open: false,
msg: ''
}
toggleDialog = () => {
if(this.state.open){
if (this.state.open) {
this.setState({ open: false, msg: '' });
}
else{
else {
this.setState({ open: !this.state });
}
}
@ -61,15 +61,15 @@ class SolutionCheck extends Component {
<Tooltip title='Lösung kontrollieren' arrow>
<IconButton
className={this.props.classes.compile}
style={{width: '40px', height: '40px', marginRight: '5px'}}
style={{ width: '40px', height: '40px', marginRight: '5px' }}
onClick={() => this.check()}
>
<FontAwesomeIcon icon={faPlay} size="xs"/>
<FontAwesomeIcon icon={faPlay} size="xs" />
</IconButton>
</Tooltip>
<Dialog
style={{zIndex: 9999999}}
style={{ zIndex: 9999999 }}
fullWidth
maxWidth={'sm'}
open={this.state.open}
@ -80,29 +80,29 @@ class SolutionCheck extends Component {
button={'Schließen'}
>
{this.state.msg.type === 'success' ?
<div style={{marginTop: '20px', display: 'flex'}}>
<div style={{ marginTop: '20px', display: 'flex' }}>
<Compile />
{this.props.activeStep === steps.length-1 ?
{this.props.activeStep === steps.length - 1 ?
<Button
style={{marginLeft: '10px'}}
style={{ marginLeft: '10px' }}
variant="contained"
color="primary"
onClick={() => {this.toggleDialog(); this.props.history.push(`/tutorial/`)}}
onClick={() => { this.toggleDialog(); this.props.history.push(`/tutorial/`) }}
>
Tutorials-Übersicht
</Button>
:
:
<Button
style={{marginLeft: '10px'}}
style={{ marginLeft: '10px' }}
variant="contained"
color="primary"
onClick={() => {this.toggleDialog(); this.props.tutorialStep(this.props.activeStep + 1)}}
onClick={() => { this.toggleDialog(); this.props.tutorialStep(this.props.activeStep + 1) }}
>
nächster Schritt
</Button>
}
</div>
: null}
: null}
</Dialog>
</div>
@ -125,4 +125,4 @@ const mapStateToProps = state => ({
xml: state.workspace.code.xml
});
export default connect(mapStateToProps, { tutorialCheck, tutorialStep })(withStyles(styles, {withTheme: true})(withRouter(SolutionCheck)));
export default connect(mapStateToProps, { tutorialCheck, tutorialStep })(withStyles(styles, { withTheme: true })(withRouter(SolutionCheck)));

View File

@ -6,7 +6,7 @@ import { withRouter } from 'react-router-dom';
import clsx from 'clsx';
import tutorials from '../../data/tutorials.json';
import tutorials from '../../data/tutorials';
import { fade } from '@material-ui/core/styles/colorManipulator';
import { withStyles } from '@material-ui/core/styles';
@ -58,31 +58,31 @@ class StepperHorizontal extends Component {
var tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other';
var title = tutorials.filter(tutorial => tutorial.id === tutorialId)[0].title;
return (
<div style={{position: 'relative'}}>
<div style={{ position: 'relative' }}>
{error || success > 0 ?
<div style={{zIndex: -1, width: error ? 'calc(100% - 40px)' : `calc(${success*100}% - 40px)`, borderRadius: success === 1 || error ? '25px' : '25px 0 0 25px', position: 'absolute', margin: 0, left: 0}} className={clsx(this.props.classes.stepper, error ? this.props.classes.stepperError : this.props.classes.stepperSuccess)}>
<div style={{ zIndex: -1, width: error ? 'calc(100% - 40px)' : `calc(${success * 100}% - 40px)`, borderRadius: success === 1 || error ? '25px' : '25px 0 0 25px', position: 'absolute', margin: 0, left: 0 }} className={clsx(this.props.classes.stepper, error ? this.props.classes.stepperError : this.props.classes.stepperSuccess)}>
</div>
: null}
: null}
{success < 1 && !error ?
<div style={{zIndex: -2, width: `calc(${(1-success)*100}% - 40px)`, borderRadius: success === 0 ? '25px' : '0px 25px 25px 0', position: 'absolute', margin: 0, right: 0}} className={clsx(this.props.classes.stepper, this.props.classes.stepperOther)}>
<div style={{ zIndex: -2, width: `calc(${(1 - success) * 100}% - 40px)`, borderRadius: success === 0 ? '25px' : '0px 25px 25px 0', position: 'absolute', margin: 0, right: 0 }} className={clsx(this.props.classes.stepper, this.props.classes.stepperOther)}>
</div>
: null}
: null}
<div className={this.props.classes.stepper}>
<Button
disabled={tutorialId === 1}
onClick={() => {this.props.history.push(`/tutorial/${tutorialId-1}`)}}
onClick={() => { this.props.history.push(`/tutorial/${tutorialId - 1}`) }}
>
{'<'}
</Button>
<Tooltip style={{display: 'flex', width: 'calc(100% - 64px - 64px)', justifyContent: 'center'}} title={title} arrow>
<Tooltip style={{ display: 'flex', width: 'calc(100% - 64px - 64px)', justifyContent: 'center' }} title={title} arrow>
<div>
{tutorialStatus !== 'Other' ? <div className={tutorialStatus === 'Success' && success === 1 ? this.props.classes.iconDivSuccess : this.props.classes.iconDivError} style={{margin: 'auto 10px auto 0'}}><FontAwesomeIcon className={this.props.classes.icon} icon={tutorialStatus === 'Success' ? faCheck : faTimes}/></div> : null}
<Typography variant='body2' style={{fontWeight: 'bold', fontSize: '1.75em', margin: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'rgba(0, 0, 0, 0.54)'}}>{title}</Typography>
{tutorialStatus !== 'Other' ? <div className={tutorialStatus === 'Success' && success === 1 ? this.props.classes.iconDivSuccess : this.props.classes.iconDivError} style={{ margin: 'auto 10px auto 0' }}><FontAwesomeIcon className={this.props.classes.icon} icon={tutorialStatus === 'Success' ? faCheck : faTimes} /></div> : null}
<Typography variant='body2' style={{ fontWeight: 'bold', fontSize: '1.75em', margin: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'rgba(0, 0, 0, 0.54)' }}>{title}</Typography>
</div>
</Tooltip>
<Button
disabled={tutorialId+1 > tutorials.length}
onClick={() => {this.props.history.push(`/tutorial/${tutorialId+1}`)}}
disabled={tutorialId + 1 > tutorials.length}
onClick={() => { this.props.history.push(`/tutorial/${tutorialId + 1}`) }}
>
{'>'}
</Button>
@ -104,4 +104,4 @@ const mapStateToProps = state => ({
currentTutorialId: state.tutorial.currentId
});
export default connect(mapStateToProps, null)(withRouter(withStyles(styles, {withTheme: true})(StepperHorizontal)));
export default connect(mapStateToProps, null)(withRouter(withStyles(styles, { withTheme: true })(StepperHorizontal)));

View File

@ -13,24 +13,24 @@ import NotFound from '../NotFound';
import { detectWhitespacesAndReturnReadableResult } from '../../helpers/whitespace';
import tutorials from '../../data/tutorials.json';
import tutorials from '../../data/tutorials';
import Card from '@material-ui/core/Card';
import Button from '@material-ui/core/Button';
class Tutorial extends Component {
componentDidMount(){
componentDidMount() {
this.props.tutorialId(Number(this.props.match.params.tutorialId));
}
componentDidUpdate(props, state){
if(props.currentTutorialId !== Number(this.props.match.params.tutorialId)){
componentDidUpdate(props, state) {
if (props.currentTutorialId !== Number(this.props.match.params.tutorialId)) {
this.props.tutorialId(Number(this.props.match.params.tutorialId));
}
}
componentWillUnmount(){
componentWillUnmount() {
this.props.tutorialId(null);
this.props.workspaceName(null);
}
@ -43,30 +43,30 @@ class Tutorial extends Component {
var name = step ? `${detectWhitespacesAndReturnReadableResult(tutorial.title)}_${detectWhitespacesAndReturnReadableResult(step.headline)}` : null;
return (
!Number.isInteger(currentTutorialId) || currentTutorialId < 1 || !tutorial ?
<NotFound button={{title: 'Zurück zur Tutorials-Übersicht', link: '/tutorial'}}/>
:
<div>
<Breadcrumbs content={[{link: '/tutorial', title: 'Tutorial'}, {link: `/tutorial/${currentTutorialId}`, title: tutorial.title}]}/>
<NotFound button={{ title: 'Zurück zur Tutorials-Übersicht', link: '/tutorial' }} />
:
<div>
<Breadcrumbs content={[{ link: '/tutorial', title: 'Tutorial' }, { link: `/tutorial/${currentTutorialId}`, title: tutorial.title }]} />
<StepperHorizontal />
<StepperHorizontal />
<div style={{display: 'flex'}}>
<StepperVertical steps={steps}/>
{/* calc(Card-padding: 10px + Button-height: 35px + Button-marginTop: 15px)*/}
<Card style={{padding: '10px 10px 60px 10px', display: 'block', position: 'relative', height: 'max-content', width: '100%'}}>
{step ?
step.type === 'instruction' ?
<Instruction step={step}/>
: <Assessment step={step} name={name}/> // if step.type === 'assessment'
: null}
<div style={{ display: 'flex' }}>
<StepperVertical steps={steps} />
{/* calc(Card-padding: 10px + Button-height: 35px + Button-marginTop: 15px)*/}
<Card style={{ padding: '10px 10px 60px 10px', display: 'block', position: 'relative', height: 'max-content', width: '100%' }}>
{step ?
step.type === 'instruction' ?
<Instruction step={step} />
: <Assessment step={step} name={name} /> // if step.type === 'assessment'
: null}
<div style={{marginTop: '20px', position: 'absolute', bottom: '10px'}}>
<Button style={{marginRight: '10px', height: '35px'}} variant='contained' disabled={this.props.activeStep === 0} onClick={() => this.props.tutorialStep(this.props.activeStep-1)}>Zurück</Button>
<Button style={{height: '35px'}}variant='contained' color='primary' disabled={this.props.activeStep === tutorial.steps.length-1} onClick={() => this.props.tutorialStep(this.props.activeStep+1)}>Weiter</Button>
</div>
</Card>
<div style={{ marginTop: '20px', position: 'absolute', bottom: '10px' }}>
<Button style={{ marginRight: '10px', height: '35px' }} variant='contained' disabled={this.props.activeStep === 0} onClick={() => this.props.tutorialStep(this.props.activeStep - 1)}>Zurück</Button>
<Button style={{ height: '35px' }} variant='contained' color='primary' disabled={this.props.activeStep === tutorial.steps.length - 1} onClick={() => this.props.tutorialStep(this.props.activeStep + 1)}>Weiter</Button>
</div>
</Card>
</div>
</div>
</div>
);
};
}

View File

@ -6,7 +6,8 @@ import clsx from 'clsx';
import Breadcrumbs from '../Breadcrumbs';
import tutorials from '../../data/tutorials.json';
import tutorials from '../../data/tutorials';
// import tutorials from '../../data/tutorials.json';
import { Link } from 'react-router-dom';
@ -54,7 +55,7 @@ class TutorialHome extends Component {
render() {
return (
<div>
<Breadcrumbs content={[{link: '/tutorial', title: 'Tutorial'}]}/>
<Breadcrumbs content={[{ link: '/tutorial', title: 'Tutorial' }]} />
<h1>Tutorial-Übersicht</h1>
<Grid container spacing={2}>
@ -62,36 +63,37 @@ class TutorialHome extends Component {
var status = this.props.status.filter(status => status.id === tutorial.id)[0];
var tasks = status.tasks;
var error = status.tasks.filter(task => task.type === 'error').length > 0;
var success = status.tasks.filter(task => task.type === 'success').length/tasks.length
var success = status.tasks.filter(task => task.type === 'success').length / tasks.length
var tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other';
return (
<Grid item xs={12} sm={6} md={4} xl={3} key={i} style={{}}>
<Link to={`/tutorial/${tutorial.id}`} style={{textDecoration: 'none', color: 'inherit'}}>
<Paper style={{height: '150px', padding: '10px', position:'relative', overflow: 'hidden'}}>
<Link to={`/tutorial/${tutorial.id}`} style={{ textDecoration: 'none', color: 'inherit' }}>
<Paper style={{ height: '150px', padding: '10px', position: 'relative', overflow: 'hidden' }}>
{tutorial.title}
<div className={clsx(this.props.classes.outerDiv)} style={{width: '160px', height: '160px', border: 0}}>
<svg style={{width: '100%', height: '100%'}}>
<div className={clsx(this.props.classes.outerDiv)} style={{ width: '160px', height: '160px', border: 0 }}>
<svg style={{ width: '100%', height: '100%' }}>
{error || success === 1 ?
<circle className={error ? this.props.classes.outerDivError : this.props.classes.outerDivSuccess} style={{transform: 'rotate(-44deg)', transformOrigin: "50% 50%"}} r="75" cx="50%" cy="50%" fill="none" stroke-width="10"></circle>
: <circle className={this.props.classes.outerDivOther} style={{transform: 'rotate(-44deg)', transformOrigin: "50% 50%"}} r="75" cx="50%" cy="50%" fill="none" stroke-width="10" stroke-dashoffset={`${(75*2*Math.PI)*(1-(50/100+success/2))}`} stroke-dasharray={`${(75*2*Math.PI)*(1-(50/100-success/2))} ${(75*2*Math.PI)*(1-(50/100+success/2))}`}></circle>}
<circle className={error ? this.props.classes.outerDivError : this.props.classes.outerDivSuccess} style={{ transform: 'rotate(-44deg)', transformOrigin: "50% 50%" }} r="75" cx="50%" cy="50%" fill="none" stroke-width="10"></circle>
: <circle className={this.props.classes.outerDivOther} style={{ transform: 'rotate(-44deg)', transformOrigin: "50% 50%" }} r="75" cx="50%" cy="50%" fill="none" stroke-width="10" stroke-dashoffset={`${(75 * 2 * Math.PI) * (1 - (50 / 100 + success / 2))}`} stroke-dasharray={`${(75 * 2 * Math.PI) * (1 - (50 / 100 - success / 2))} ${(75 * 2 * Math.PI) * (1 - (50 / 100 + success / 2))}`}></circle>}
{success < 1 && !error ?
<circle className={this.props.classes.outerDivSuccess} style={{transform: 'rotate(-44deg)', transformOrigin: "50% 50%"}} r="75" cx="50%" cy="50%" fill="none" stroke-width="10" stroke-dashoffset={`${(75*2*Math.PI)*(1-(50/100+success/2))}`} stroke-dasharray={`${(75*2*Math.PI)}`}>
<circle className={this.props.classes.outerDivSuccess} style={{ transform: 'rotate(-44deg)', transformOrigin: "50% 50%" }} r="75" cx="50%" cy="50%" fill="none" stroke-width="10" stroke-dashoffset={`${(75 * 2 * Math.PI) * (1 - (50 / 100 + success / 2))}`} stroke-dasharray={`${(75 * 2 * Math.PI)}`}>
</circle>
: null}
: null}
</svg>
</div>
<div className={clsx(this.props.classes.outerDiv, tutorialStatus === 'Error' ? this.props.classes.outerDivError : tutorialStatus === 'Success' ? this.props.classes.outerDivSuccess : null)}>
<div className={this.props.classes.innerDiv}>
{error || success === 1 ?
<FontAwesomeIcon size='4x' icon={tutorialStatus === 'Success' ? faCheck : faTimes}/>
: <Typography variant='h3' className={success > 0 ? this.props.classes.outerDivSuccess : {}}>{Math.round(success*100)}%</Typography>
<FontAwesomeIcon size='4x' icon={tutorialStatus === 'Success' ? faCheck : faTimes} />
: <Typography variant='h3' className={success > 0 ? this.props.classes.outerDivSuccess : {}}>{Math.round(success * 100)}%</Typography>
}
</div>
</div>
</Paper>
</Link>
</Grid>
)})}
)
})}
</Grid>
</div>
);
@ -108,4 +110,4 @@ const mapStateToProps = state => ({
status: state.tutorial.status
});
export default connect(mapStateToProps, null)(withStyles(styles, {withTheme: true})(TutorialHome));
export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(TutorialHome));

View File

@ -0,0 +1 @@
{"id":1602160534286,"title":"Erste Schritte","steps":[{"id":1,"type":"instruction","headline":"Erste Schritte","text":"In diesem Tutorial lernst du die ersten Schritte mit der senseBox kennen. Du erstellst ein erstes Programm, baust einen ersten Schaltkreis auf und lernst, wie du das Programm auf die senseBox MCU überträgst.","hardware":["senseboxmcu","led","breadboard","jst-adapter","resistor-470ohm"],"requirements":[]},{"id":2,"type":"instruction","headline":"Aufbau der Schaltung","text":"Stecke die LED auf das Breadboard und verbinde diese mithile des Widerstandes und dem JST Kabel mit dem Port Digital/Analog 1."},{"id":3,"type":"instruction","headline":"Programmierung","text":"Jedes Programm für die senseBox besteht aus zwei Funktionen. Die Setup () Funktion wird zu Begin einmalig ausgeführt und der Programmcode Schrittweise ausgeführt. Nachdem die Setup () Funktion durchlaufen worden ist wird der Programmcode aus der zweiten Funktion, der Endlosschleife, fortlaufend wiederholt.","xml":"<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" deletable=\"false\" x=\"27\" y=\"16\"></block>\n</xml>"},{"id":4,"type":"instruction","headline":"Leuchten der LED","text":"Um nun die LED zum leuchten zu bringen wird folgender Block in die Endlosschleife eingefügt. Der Block bietet dir auszuwählen an welchen Pin die LED angeschlossen wurd und ob diese ein oder ausgeschaltet werden soll.","xml":"<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"sensebox_led\" id=\"S_GEYN/`Z]?{5:aKp6e^\" x=\"21\" y=\"27\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n </block>\n</xml>"},{"id":5,"type":"task","headline":"Aufgabe 1","text":"Verwende den zuvor kennengelernten Block, um die LED zum leuchten zu bringen.","xml":"<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" deletable=\"false\" x=\"27\" y=\"16\">\n <statement name=\"LOOP_FUNC\">\n <block type=\"sensebox_led\" id=\"S#27sNt/*c:[Zv+YyzD9\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n </block>\n </statement>\n </block>\n</xml>"},{"id":6,"type":"instruction","headline":"Programmcode übertragen","text":"Super! Du hast dein erstes Programm erstellt und kompiliert. In nächsten Schritt muss das Programm auf deine senseBox MCU übertragen werden. Schließe diese mithilfe des USB Kabel an deinem Computer an und drücke 2 mal schnell hintereinander auf den roten Reset Button, um die senseBox MCU in den Lernmodus zu versetzten. Die senseBox MCU erscheint nun als USB-Gerät in deinem Dateiexplorer. Kopiere anschließend das Programm auf die senseBox MCU.","media":{"youtube":"jzlOJ7Zuqqw"}}]}

View File

@ -0,0 +1,44 @@
{
"id": 1602160884767,
"title": "Wenn-Dann Bedingungen",
"steps": [
{
"id": 1,
"type": "instruction",
"headline": "Wenn-Dann-Was?",
"text": "In diesem Tutorial lernst du die Verwendung von Wenn-Dann Bedingungen kennen. Die Wenn-Dann Bedingung ist eine der wichtigsten Kontrollstrukturen in der Programmierung und hilft dir dabei auf bestimmte Zustände einzugehen. ",
"hardware": [
"senseboxmcu",
"breadboard",
"jst-adapter",
"resistor-470ohm"
],
"requirements": [
1602160534286
]
},
{
"id": 2,
"type": "instruction",
"headline": "Aufbau der Hardware",
"text": "Verbinde die LED mit Hilfe des JST-Adapter Kabel und dem 470 Ohm Widerstand mit einem der 3 digital/analog Ports der senseBox MCU. ",
"media": {
"picture": "01_circuit.png"
}
},
{
"id": 3,
"type": "instruction",
"headline": "Die Wenn-Dann Bedingung",
"text": "Eine Wenn-Dann Bedingung kann dazu verwendet werden bestimmten Zeilen Code auszuführen, wenn eine bestimmte Bedingung erfüllt ist. \n\nwenn Bedingung dann\n Anweisung(en)\nende\n\nDas Beispiel unten zeigt dir wie eine Wenn-Dann Bedingung aufgebaut ist. Es kann zum Beispiel die Temperatur mit einem Wert verglichen werden. Ist dieser Vergleich wahr (true), dann werden die Blöcke ausgeführt, die neben \"dann\" stehen ausgeführt. Ist die Bedingung nicht erfüllt (false) dann wird einfach der Programmcode unterhalb der Wenn-Dann Bedingung ausgeführt.\n",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" x=\"27\" y=\"16\">\n <statement name=\"LOOP_FUNC\">\n <block type=\"controls_if\" id=\"yu|L-MD~uP9vg@9F}Lu(\">\n <value name=\"IF0\">\n <block type=\"logic_compare\" id=\"~P6$8C+4++}u[iIr#%2-\">\n <field name=\"OP\">EQ</field>\n <value name=\"A\">\n <block type=\"sensebox_sensor_temp_hum\" id=\"Wy!eu6l0F#5ST~!$T*^W\">\n <field name=\"NAME\">Temperature</field>\n </block>\n </value>\n </block>\n </value>\n </block>\n </statement>\n </block>\n <block type=\"math_number\" id=\"n1maf$o[.I3`ce3D}]/q\" x=\"644\" y=\"180\">\n <field name=\"NUM\">0</field>\n </block>\n</xml>"
},
{
"id": 4,
"type": "task",
"headline": "Leuchten der LED auf Knopfdruck",
"text": "Lasse die LED leuchten, wenn der \"Button\" auf der senseBox MCU gedrückt wird. Den Block für den Button findest du unter \"Sensoren\".",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" deletable=\"false\" x=\"27\" y=\"16\">\n <statement name=\"LOOP_FUNC\">\n <block type=\"controls_if\" id=\"^HaPX`(}uMFuMD~k1ao9\">\n <mutation elseif=\"1\"></mutation>\n <value name=\"IF0\">\n <block type=\"logic_compare\" id=\"|1,]aWRGs/Wp0}YQ{Ln{\">\n <field name=\"OP\">EQ</field>\n <value name=\"A\">\n <block type=\"sensebox_button\" id=\",@aRkh3M9K(DcYP$A(:[\">\n <field name=\"FUNCTION\">isPressed</field>\n <field name=\"PIN\">0</field>\n </block>\n </value>\n <value name=\"B\">\n <block type=\"logic_boolean\" id=\"ZjXsPgomX!cIM8bll!9;\">\n <field name=\"BOOL\">TRUE</field>\n </block>\n </value>\n </block>\n </value>\n <statement name=\"DO0\">\n <block type=\"sensebox_led\" id=\"fTE4,Kwz5s8uHaE/k:!h\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n </block>\n </statement>\n <value name=\"IF1\">\n <block type=\"logic_compare\" id=\"v6U9{L$*?0)r.8qG1$gn\">\n <field name=\"OP\">EQ</field>\n <value name=\"A\">\n <block type=\"sensebox_button\" id=\"yLypRfX$0DzgPYw8F/q#\">\n <field name=\"FUNCTION\">isPressed</field>\n <field name=\"PIN\">0</field>\n </block>\n </value>\n <value name=\"B\">\n <block type=\"logic_boolean\" id=\"r0c0q~2^GQo7DDjPv1.C\">\n <field name=\"BOOL\">FALSE</field>\n </block>\n </value>\n </block>\n </value>\n <statement name=\"DO1\">\n <block type=\"sensebox_led\" id=\"Zx0)_+JAGfG~b`.fYOIl\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">LOW</field>\n </block>\n </statement>\n </block>\n </statement>\n </block>\n</xml>"
}
]
}

50
src/data/loops_01.json Normal file
View File

@ -0,0 +1,50 @@
{
"id": 1602162097684,
"title": "Schleifen",
"steps": [
{
"id": 1,
"type": "instruction",
"headline": "Schleifen",
"text": "In diesem Tutorial wird die Verwendung von Schleifen eingeführt. Eine Schleife (auch „Wiederholung“ oder englisch loop) ist eine Kontrollstruktur in Programmiersprachen. Sie wiederholt einen Anweisungs-Block den sogenannten Schleifenrumpf oder Schleifenkörper , solange die Schleifenbedingung als Laufbedingung gültig bleibt bzw. als Abbruchbedingung nicht eintritt. Schleifen, deren Schleifenbedingung immer zur Fortsetzung führt oder die keine Schleifenbedingung haben, sind Endlosschleifen. Die Endlosschleife hast du bereits im ersten Tutorial \"Erste Schritte\" kennengelernt",
"hardware": [
"breadboard",
"jst-adapter",
"senseboxmcu",
"led",
"resistor-470ohm"
],
"requirements": [
1602160534286
]
},
{
"id": 2,
"type": "instruction",
"headline": "Verwendung von Schleifen",
"text": "Die Blöcke findest du in der Kategorie \"Schleifen\". Die einfachste Schleife, die du Verwenden kannst, ist der Block \"Wiederhole 10 mal\". Bei diesem Block kannst du die Blöcke, die eine bestimmte Zahl wiederholt werden soll einfach in den offenen Block abschnitt ziehen. ",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"controls_repeat_ext\" id=\"!|`dyF$`~*!l~D[TUc4N\" x=\"38\" y=\"32\">\n <value name=\"TIMES\">\n <block type=\"math_number\" id=\"ktgQ[7pD~M{sq;r^kLuz\">\n <field name=\"NUM\">10</field>\n </block>\n </value>\n </block>\n</xml>"
},
{
"id": 3,
"type": "task",
"headline": "Verwendung von Schleifen",
"text": "Lass die LED genau 5 mal in einem Abstand von 1000 Millisekunden blinken. Anschließend soll die LED ausgeschaltet werden. ",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" deletable=\"false\" x=\"18\" y=\"18\">\n <statement name=\"SETUP_FUNC\">\n <block type=\"controls_repeat_ext\" id=\"_d{J^FWUT8M?}o[/6Fpj\">\n <value name=\"TIMES\">\n <block type=\"math_number\" id=\"qao_;lzo~kE?25HM*kJ+\">\n <field name=\"NUM\">5</field>\n </block>\n </value>\n <statement name=\"DO\">\n <block type=\"sensebox_led\" id=\"A)}4:wR_79zAHUBq5?1j\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n <next>\n <block type=\"time_delay\" id=\"Yfn;,|wxaRhium=T[-wM\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"math_number\" id=\"z=1f4hMm_Q~e-+Wvh,S|\">\n <field name=\"NUM\">1000</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_led\" id=\"nu2%x%_iigf]r$FJ7XEw\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">LOW</field>\n <next>\n <block type=\"time_delay\" id=\"4@Y4E|ewWB)([vf/4Ttn\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"math_number\" id=\"l,ZAG8;|Uv^:P5/FOjwD\">\n <field name=\"NUM\">1000</field>\n </block>\n </value>\n </block>\n </next>\n </block>\n </next>\n </block>\n </next>\n </block>\n </statement>\n </block>\n </statement>\n </block>\n</xml>"
},
{
"id": 4,
"type": "instruction",
"headline": "Schleife mit Laufzeitvariable",
"text": "Mit diesem Block lässt sich die Schleife noch genauer Steuern und beeinflussen. Hierbei wird nicht nur angegeben wie oft die Anweisungen innerhalb der Schleife wiederholt werden sondern auch welche Variable zum zählen verwendet wird und in welchen Schritten gezählt werden soll. Der Vorteil bei diesem Block ist, dass die Wert der Variable auch innerhalb der Anweisungen verwendet werden kann. So kannst zum Beispiel die Variable \"i\" verwenden um die Blinkfrequenz zu beeinflussen.",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <variables>\n <variable id=\")~{j#yh!vpAj!r)xZ%r`\">i</variable>\n </variables>\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" x=\"27\" y=\"16\">\n <statement name=\"LOOP_FUNC\">\n <block type=\"controls_for\" id=\"FYj`$_+6-llMr!1pbbGa\">\n <field name=\"VAR\" id=\")~{j#yh!vpAj!r)xZ%r`\">i</field>\n <value name=\"FROM\">\n <block type=\"math_number\" id=\"d`8}:TQSpqu@$),hheqW\">\n <field name=\"NUM\">100</field>\n </block>\n </value>\n <value name=\"TO\">\n <block type=\"math_number\" id=\"SugsqbJBjnV.+wt,l*os\">\n <field name=\"NUM\">1000</field>\n </block>\n </value>\n <value name=\"BY\">\n <block type=\"math_number\" id=\"A.r{E[gVmy`GOH[/UjOd\">\n <field name=\"NUM\">100</field>\n </block>\n </value>\n <statement name=\"DO\">\n <block type=\"sensebox_led\" id=\",|)Qs}dfbh`hTL#2:vEr\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n <next>\n <block type=\"time_delay\" id=\"P!noJ-RN{(E{=P!1c-un\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"variables_get_dynamic\" id=\"bd;B*4HgU:~Vb2kQ9qh.\">\n <field name=\"VAR\" id=\")~{j#yh!vpAj!r)xZ%r`\">i</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_led\" id=\"_k?%f6^b0WNYifw]yrd7\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">LOW</field>\n <next>\n <block type=\"time_delay\" id=\"#BYOOJXBj%)0op!76)z=\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"variables_get_dynamic\" id=\"(cg$kq?jc~Zi`6WosPN5\">\n <field name=\"VAR\" id=\")~{j#yh!vpAj!r)xZ%r`\">i</field>\n </block>\n </value>\n </block>\n </next>\n </block>\n </next>\n </block>\n </next>\n </block>\n </statement>\n </block>\n </statement>\n </block>\n</xml>"
},
{
"id": 5,
"type": "task",
"headline": "Schleifen mit Laufzeitvariable",
"text": "Verwende die Schleife mit Laufzeitvariable und beeinflusse die Blinkfrequenz mithilfe dem Wert der Variable.",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <variables>\n <variable id=\"*$)UUMo%aBec4cQTwHGx\">i</variable>\n </variables>\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" deletable=\"false\" x=\"53\" y=\"10\">\n <statement name=\"LOOP_FUNC\">\n <block type=\"controls_for\" id=\"K(eQur,H3CCpZ4aKVehO\">\n <field name=\"VAR\" id=\"*$)UUMo%aBec4cQTwHGx\">i</field>\n <value name=\"FROM\">\n <block type=\"math_number\" id=\"J^UJb^1-SukkH7yBbynj\">\n <field name=\"NUM\">100</field>\n </block>\n </value>\n <value name=\"TO\">\n <block type=\"math_number\" id=\"azP7sh9f,v$9VH[.9BCh\">\n <field name=\"NUM\">1000</field>\n </block>\n </value>\n <value name=\"BY\">\n <block type=\"math_number\" id=\"#j`dag~sK!]D#{_;._)%\">\n <field name=\"NUM\">100</field>\n </block>\n </value>\n <statement name=\"DO\">\n <block type=\"sensebox_led\" id=\"H6+{(4h(}a[yr5w,f(`,\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n <next>\n <block type=\"time_delay\" id=\"vPfaX^j~7;YwT+A!y@**\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"variables_get_dynamic\" id=\"1}5O.el5^EHJCM`,oa4-\">\n <field name=\"VAR\" id=\"*$)UUMo%aBec4cQTwHGx\">i</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_led\" id=\"TRn{}$-@^eXI?eGfWJ/{\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">LOW</field>\n <next>\n <block type=\"time_delay\" id=\"x@]F,29zp}87PA|2+eoJ\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"variables_get_dynamic\" id=\"WF.bP/9w[WlFoF*2;*{G\">\n <field name=\"VAR\" id=\"*$)UUMo%aBec4cQTwHGx\">i</field>\n </block>\n </value>\n </block>\n </next>\n </block>\n </next>\n </block>\n </next>\n </block>\n </statement>\n </block>\n </statement>\n </block>\n <block type=\"math_number\" id=\"!42y97D|+Vi9*DKE^5p`\" disabled=\"true\" x=\"198\" y=\"184\">\n <field name=\"NUM\">1000</field>\n </block>\n</xml>"
}
]
}

9
src/data/tutorials.js Normal file
View File

@ -0,0 +1,9 @@
let tutorials = [
require('./ErsteSchritte.json'),
require('./loops_01.json'),
require('./Wenn-DannBedingungen.json'),
require('./variablen_01.json'),
]
module.exports = tutorials;

View File

@ -1,151 +0,0 @@
[
{
"id": 1,
"title": "Erste Schritte",
"steps": [
{
"id": 1,
"type": "instruction",
"headline": "Erste Schritte",
"text": "In diesem Tutorial lernst du die ersten Schritte mit der senseBox kennen. Du erstellst ein erstes Programm, baust einen ersten Schaltkreis auf und lernst, wie du das Programm auf die senseBox MCU überträgst.",
"hardware": [
"senseboxmcu",
"led",
"breadboard",
"jst-adapter",
"resistor-470ohm"
],
"requirements": []
},
{
"id": 2,
"type": "instruction",
"headline": "Aufbau der Schaltung",
"text": "Stecke die LED auf das Breadboard und verbinde diese mithile des Widerstandes und dem JST Kabel mit dem Port Digital/Analog 1."
},
{
"id": 3,
"type": "instruction",
"headline": "Programmierung",
"text": "Jedes Programm für die senseBox besteht aus zwei Funktionen. Die Setup () Funktion wird zu Begin einmalig ausgeführt und der Programmcode Schrittweise ausgeführt. Nachdem die Setup () Funktion durchlaufen worden ist wird der Programmcode aus der zweiten Funktion, der Endlosschleife, fortlaufend wiederholt.",
"xml": "<xml xmlns='https://developers.google.com/blockly/xml'><block type='arduino_functions' id='QWW|$jB8+*EL;}|#uA' deletable='false' x='27' y='16'></block></xml>"
},
{
"id": 4,
"type": "instruction",
"headline": "Leuchten der LED",
"text": "Um nun die LED zum leuchten zu bringen wird folgender Block in die Endlosschleife eingefügt. Der Block bietet dir auszuwählen an welchen Pin die LED angeschlossen wurd und ob diese ein oder ausgeschaltet werden soll.",
"xml": "<xml xmlns='https://developers.google.com/blockly/xml'><block type='arduino_functions' id='QWW|$jB8+*EL;}|#uA' deletable='false' x='27' y='16'></block></xml>"
},
{
"id": 5,
"type": "task",
"headline": "Aufgabe 1",
"text": "Verwenden den Block zum leuchten der LED und übertrage dein erstes Programm auf die senseBox MCU.",
"xml": "<xml xmlns='https://developers.google.com/blockly/xml'><block type='arduino_functions' id='QWW|$jB8+*EL;}|#uA' deletable='false' x='27' y='16'></block></xml>"
}
]
},
{
"id": 2,
"title": "Schleifen",
"steps": [
{
"id": 1,
"type": "instruction",
"headline": "Schleifen",
"text": "In diesem Tutorial wird die Verwendung von Schleifen eingeführt. Eine Schleife (auch „Wiederholung“ oder englisch loop) ist eine Kontrollstruktur in Programmiersprachen. Sie wiederholt einen Anweisungs-Block den sogenannten Schleifenrumpf oder Schleifenkörper , solange die Schleifenbedingung als Laufbedingung gültig bleibt bzw. als Abbruchbedingung nicht eintritt. Schleifen, deren Schleifenbedingung immer zur Fortsetzung führt oder die keine Schleifenbedingung haben, sind Endlosschleifen. Die Endlosschleife hast du bereits im ersten Tutorial \"Erste Schritte\" kennengelernt",
"hardware": [
"breadboard",
"jst-adapter",
"senseboxmcu",
"led",
"resistor-470ohm"
],
"requirements": [
1
]
},
{
"id": 2,
"type": "instruction",
"headline": "Verwendung von Schleifen",
"text": "Die Blöcke findest du in der Kategorie \"Schleifen\". Die einfachste Schleife, die du Verwenden kannst, ist der Block \"Wiederhole 10 mal\". Bei diesem Block kannst du die Blöcke, die eine bestimmte Zahl wiederholt werden soll einfach in den offenen Block abschnitt ziehen. ",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"controls_repeat_ext\" id=\"!|`dyF$`~*!l~D[TUc4N\" x=\"38\" y=\"32\">\n <value name=\"TIMES\">\n <block type=\"math_number\" id=\"ktgQ[7pD~M{sq;r^kLuz\">\n <field name=\"NUM\">10</field>\n </block>\n </value>\n </block>\n</xml>"
},
{
"id": 3,
"type": "task",
"headline": "Verwendung von Schleifen",
"text": "Lass die LED genau 5 mal in einem Abstand von 1000 Millisekunden blinken. Anschließend soll die LED ausgeschaltet werden. ",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" deletable=\"false\" x=\"18\" y=\"18\">\n <statement name=\"SETUP_FUNC\">\n <block type=\"controls_repeat_ext\" id=\"_d{J^FWUT8M?}o[/6Fpj\">\n <value name=\"TIMES\">\n <block type=\"math_number\" id=\"qao_;lzo~kE?25HM*kJ+\">\n <field name=\"NUM\">5</field>\n </block>\n </value>\n <statement name=\"DO\">\n <block type=\"sensebox_led\" id=\"A)}4:wR_79zAHUBq5?1j\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n <next>\n <block type=\"time_delay\" id=\"Yfn;,|wxaRhium=T[-wM\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"math_number\" id=\"z=1f4hMm_Q~e-+Wvh,S|\">\n <field name=\"NUM\">1000</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_led\" id=\"nu2%x%_iigf]r$FJ7XEw\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">LOW</field>\n <next>\n <block type=\"time_delay\" id=\"4@Y4E|ewWB)([vf/4Ttn\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"math_number\" id=\"l,ZAG8;|Uv^:P5/FOjwD\">\n <field name=\"NUM\">1000</field>\n </block>\n </value>\n </block>\n </next>\n </block>\n </next>\n </block>\n </next>\n </block>\n </statement>\n </block>\n </statement>\n </block>\n</xml>"
},
{
"id": 4,
"type": "instruction",
"headline": "Schleife mit Laufzeitvariable",
"text": "Mit diesem Block lässt sich die Schleife noch genauer Steuern und beeinflussen. Hierbei wird nicht nur angegeben wie oft die Anweisungen innerhalb der Schleife wiederholt werden sondern auch welche Variable zum zählen verwendet wird und in welchen Schritten gezählt werden soll. Der Vorteil bei diesem Block ist, dass die Wert der Variable auch innerhalb der Anweisungen verwendet werden kann. So kannst zum Beispiel die Variable \"i\" verwenden um die Blinkfrequenz zu beeinflussen.",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <variables>\n <variable id=\")~{j#yh!vpAj!r)xZ%r`\">i</variable>\n </variables>\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" x=\"27\" y=\"16\">\n <statement name=\"LOOP_FUNC\">\n <block type=\"controls_for\" id=\"FYj`$_+6-llMr!1pbbGa\">\n <field name=\"VAR\" id=\")~{j#yh!vpAj!r)xZ%r`\">i</field>\n <value name=\"FROM\">\n <block type=\"math_number\" id=\"d`8}:TQSpqu@$),hheqW\">\n <field name=\"NUM\">100</field>\n </block>\n </value>\n <value name=\"TO\">\n <block type=\"math_number\" id=\"SugsqbJBjnV.+wt,l*os\">\n <field name=\"NUM\">1000</field>\n </block>\n </value>\n <value name=\"BY\">\n <block type=\"math_number\" id=\"A.r{E[gVmy`GOH[/UjOd\">\n <field name=\"NUM\">100</field>\n </block>\n </value>\n <statement name=\"DO\">\n <block type=\"sensebox_led\" id=\",|)Qs}dfbh`hTL#2:vEr\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n <next>\n <block type=\"time_delay\" id=\"P!noJ-RN{(E{=P!1c-un\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"variables_get_dynamic\" id=\"bd;B*4HgU:~Vb2kQ9qh.\">\n <field name=\"VAR\" id=\")~{j#yh!vpAj!r)xZ%r`\">i</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_led\" id=\"_k?%f6^b0WNYifw]yrd7\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">LOW</field>\n <next>\n <block type=\"time_delay\" id=\"#BYOOJXBj%)0op!76)z=\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"variables_get_dynamic\" id=\"(cg$kq?jc~Zi`6WosPN5\">\n <field name=\"VAR\" id=\")~{j#yh!vpAj!r)xZ%r`\">i</field>\n </block>\n </value>\n </block>\n </next>\n </block>\n </next>\n </block>\n </next>\n </block>\n </statement>\n </block>\n </statement>\n </block>\n</xml>"
},
{
"id": 5,
"type": "task",
"headline": "Schleifen mit Laufzeitvariable",
"text": "Verwende die Schleife mit Laufzeitvariable und beeinflusse die Blinkfrequenz mithilfe dem Wert der Variable.",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <variables>\n <variable id=\"*$)UUMo%aBec4cQTwHGx\">i</variable>\n </variables>\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" deletable=\"false\" x=\"53\" y=\"10\">\n <statement name=\"LOOP_FUNC\">\n <block type=\"controls_for\" id=\"K(eQur,H3CCpZ4aKVehO\">\n <field name=\"VAR\" id=\"*$)UUMo%aBec4cQTwHGx\">i</field>\n <value name=\"FROM\">\n <block type=\"math_number\" id=\"J^UJb^1-SukkH7yBbynj\">\n <field name=\"NUM\">100</field>\n </block>\n </value>\n <value name=\"TO\">\n <block type=\"math_number\" id=\"azP7sh9f,v$9VH[.9BCh\">\n <field name=\"NUM\">1000</field>\n </block>\n </value>\n <value name=\"BY\">\n <block type=\"math_number\" id=\"#j`dag~sK!]D#{_;._)%\">\n <field name=\"NUM\">100</field>\n </block>\n </value>\n <statement name=\"DO\">\n <block type=\"sensebox_led\" id=\"H6+{(4h(}a[yr5w,f(`,\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">HIGH</field>\n <next>\n <block type=\"time_delay\" id=\"vPfaX^j~7;YwT+A!y@**\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"variables_get_dynamic\" id=\"1}5O.el5^EHJCM`,oa4-\">\n <field name=\"VAR\" id=\"*$)UUMo%aBec4cQTwHGx\">i</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_led\" id=\"TRn{}$-@^eXI?eGfWJ/{\">\n <field name=\"PIN\">1</field>\n <field name=\"STAT\">LOW</field>\n <next>\n <block type=\"time_delay\" id=\"x@]F,29zp}87PA|2+eoJ\">\n <value name=\"DELAY_TIME_MILI\">\n <block type=\"variables_get_dynamic\" id=\"WF.bP/9w[WlFoF*2;*{G\">\n <field name=\"VAR\" id=\"*$)UUMo%aBec4cQTwHGx\">i</field>\n </block>\n </value>\n </block>\n </next>\n </block>\n </next>\n </block>\n </next>\n </block>\n </statement>\n </block>\n </statement>\n </block>\n <block type=\"math_number\" id=\"!42y97D|+Vi9*DKE^5p`\" disabled=\"true\" x=\"198\" y=\"184\">\n <field name=\"NUM\">1000</field>\n </block>\n</xml>"
}
]
},
{
"id": 3,
"title": "WLAN einrichten",
"steps": [
{
"id": 1,
"type": "instruction",
"headline": "Einführung",
"text": "In diesem Tutorial lernst du wie man die senseBox mit dem Internet verbindest.",
"hardware": [
"senseboxmcu",
"wifi-bee"
],
"requirements": [
1
]
},
{
"id": 2,
"type": "instruction",
"headline": "Programmierung",
"text": "Man benötigt folgenden Block:",
"media": {
"picture": "block_en.svg"
},
"xml": "<xml xmlns='https://developers.google.com/blockly/xml'><block type='sensebox_wifi' id='-!X.Ay]z1ACt!f5+Vfr8'><field name='SSID'>SSID</field><field name='Password'>Password</field></block></xml>"
},
{
"id": 3,
"type": "instruction",
"headline": "Block richtig einbinden",
"text": "Dies ist ein Test.",
"media": {
"youtube": "sf3RzXq6iVo"
}
},
{
"id": 4,
"type": "task",
"headline": "Aufgabe 1",
"text": "Stelle eine WLAN-Verbindung mit einem beliebigen Netzwerk her.",
"xml": "<xml xmlns='https://developers.google.com/blockly/xml'><block type='arduino_functions' id='QWW|$jB8+*EL;}|#uA' deletable='false' x='27' y='16'><statement name='SETUP_FUNC'><block type='sensebox_wifi' id='W}P2Y^g,muH@]|@anou}'><field name='SSID'>SSID</field><field name='Password'>Password</field></block></statement></block></xml>"
},
{
"id": 5,
"type": "task",
"headline": "Aufgabe 2",
"text": "Versuche das gleiche einfach nochmal. Übung macht den Meister! ;)",
"xml": "<xml xmlns='https://developers.google.com/blockly/xml'><block type='arduino_functions' id='QWW|$jB8+*EL;}|#uA' deletable='false' x='27' y='16'><statement name='SETUP_FUNC'><block type='sensebox_wifi' id='W}P2Y^g,muH@]|@anou}'><field name='SSID'>SSID</field><field name='Password'>Password</field></block></statement></block></xml>"
}
]
}
]

View File

@ -0,0 +1,41 @@
{
"id": 1602162172424,
"title": "Verwendung von Variablen",
"steps": [
{
"id": 1,
"type": "instruction",
"headline": "Variablen",
"text": "Variablen, auch Platzhalter genannt, werden in der Informatik für verschiedene Dinge genutzt. Sie sind eine Art Kiste, die mit einem Namen versehen ist. In dieser Kiste kannst du verschiedene Dinge hinterlegen (z.B. Zahlen und Texte) und diese später wieder abrufen. ",
"hardware": [
"senseboxmcu",
"oled",
"jst-jst",
"hdc1080"
],
"requirements": [
1602160534286
]
},
{
"id": 2,
"type": "instruction",
"headline": "Aufbau der Hardware",
"text": "Verbinde das Display und den Temperatur- und Luftfeuchtigkeitssensor jeweils mit einem JST-JST Kabel mit einem der 5 I2C Ports auf der senseBox MCU."
},
{
"id": 3,
"type": "instruction",
"headline": "Variablen in Blockly",
"text": "Variablen können ihren Wert im Laufe des Programmes verändern, sodass du zum Beispiel der Variable „Temperatur“ immer den aktuell gemessenen Temperaturwert zuweist. Eine Variablen besitzt immer einer bestimmten Datentyp. Der Datentyp gibt im Endeffekt an, wie groß die Kiste ist und wie der Wert aussehen kann, der in der Variable gespeichert werden kann. \n\nVariablen - Datentypen\nJe nachdem, was du in einer Variable speichern möchtest, musst du den richten Datentyp auswählen.\nZeichen: Für einzelne Textzeichen\nText: Für ganze Wörter oder Sätze\nZahl: Für Zahlen von -32768 bis +32768\nGroße Zahl: Für Zahlen von -2147483648 bis\n +2147483648\nDezimalzahl: Für Kommazahlen (z.B. 25,3)",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <variables>\n <variable type=\"float\" id=\"s/vc$u-EyvVr.Nj(m}Qu\">Temperatur</variable>\n </variables>\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" x=\"27\" y=\"16\">\n <statement name=\"SETUP_FUNC\">\n <block type=\"sensebox_display_beginDisplay\" id=\"`tj@e,u63(d3fs/K71`A\"></block>\n </statement>\n <statement name=\"LOOP_FUNC\">\n <block type=\"variables_set_dynamic\" id=\"9.`Dj,eU7L15ZD{o2c^{\">\n <field name=\"VAR\" id=\"s/vc$u-EyvVr.Nj(m}Qu\" variabletype=\"float\">Temperatur</field>\n <value name=\"VALUE\">\n <block type=\"sensebox_sensor_temp_hum\" id=\"Ml7-3urP[?9L4k71u5/f\">\n <field name=\"NAME\">Temperature</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_display_show\" id=\"J[dD~4AnHRXYq3qK5^{z\">\n <statement name=\"SHOW\">\n <block type=\"sensebox_display_printDisplay\" id=\"sg25UA3mp72*zt_.hgR%\">\n <field name=\"COLOR\">WHITE,BLACK</field>\n <field name=\"SIZE\">1</field>\n <field name=\"X\">0</field>\n <field name=\"Y\">0</field>\n </block>\n </statement>\n </block>\n </next>\n </block>\n </statement>\n </block>\n <block type=\"variables_get_dynamic\" id=\"Pi0a=LwOd]Qx1}q[QljS\" x=\"252\" y=\"336\">\n <field name=\"VAR\" id=\"s/vc$u-EyvVr.Nj(m}Qu\" variabletype=\"float\">Temperatur</field>\n </block>\n</xml>"
},
{
"id": 4,
"type": "task",
"headline": "Aufgabe 1:",
"text": "Erste 2 Variablen vom Typ \"Kommazahl\" und weise die Werte für Temperatur und Luftfeuchtigkeit zu. Lasse anschließend den Wert der Variablen auf dem Display anzeigen.",
"xml": "<xml xmlns=\"https://developers.google.com/blockly/xml\">\n <variables>\n <variable type=\"float\" id=\";%_Xz2^W|D@`pj|28,!5\">Temperatur</variable>\n <variable type=\"float\" id=\"[EbZeJmVDy_yf%nn}kg`\">Luftfeuchte</variable>\n </variables>\n <block type=\"arduino_functions\" id=\"QWW|$jB8+*EL;}|#uA\" deletable=\"false\" x=\"27\" y=\"16\">\n <statement name=\"SETUP_FUNC\">\n <block type=\"sensebox_display_beginDisplay\" id=\"#v)|Z-SGAvCfh+tW*u:b\"></block>\n </statement>\n <statement name=\"LOOP_FUNC\">\n <block type=\"variables_set_dynamic\" id=\"4(3O[5o69|]D[1(`OS77\">\n <field name=\"VAR\" id=\";%_Xz2^W|D@`pj|28,!5\" variabletype=\"float\">Temperatur</field>\n <value name=\"VALUE\">\n <block type=\"sensebox_sensor_temp_hum\" id=\"lqo)Q_WfPH/)#n?Kejw9\">\n <field name=\"NAME\">Temperature</field>\n </block>\n </value>\n <next>\n <block type=\"variables_set_dynamic\" id=\"wZ([1QpBf,W-+ML8$yE^\">\n <field name=\"VAR\" id=\"[EbZeJmVDy_yf%nn}kg`\" variabletype=\"float\">Luftfeuchte</field>\n <value name=\"VALUE\">\n <block type=\"sensebox_sensor_temp_hum\" id=\"N@rK(BhH:*:,7?gd0znv\">\n <field name=\"NAME\">Temperature</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_display_show\" id=\")HB9?-!GebOtxFJ*UF9:\">\n <statement name=\"SHOW\">\n <block type=\"sensebox_display_printDisplay\" id=\"U90y}?~Cvp?uS2KfL#Ph\">\n <field name=\"COLOR\">WHITE,BLACK</field>\n <field name=\"SIZE\">1</field>\n <field name=\"X\">0</field>\n <field name=\"Y\">0</field>\n <value name=\"printDisplay\">\n <block type=\"variables_get_dynamic\" id=\"+.p4#smC?kl[1{cjt70R\">\n <field name=\"VAR\" id=\"[EbZeJmVDy_yf%nn}kg`\" variabletype=\"float\">Luftfeuchte</field>\n </block>\n </value>\n <next>\n <block type=\"sensebox_display_printDisplay\" id=\"GfS)4y,Y7[nm[KUW*uGL\">\n <field name=\"COLOR\">WHITE,BLACK</field>\n <field name=\"SIZE\">1</field>\n <field name=\"X\">0</field>\n <field name=\"Y\">20</field>\n <value name=\"printDisplay\">\n <block type=\"variables_get_dynamic\" id=\"wWCpBCPp9i%M13l9H^Et\">\n <field name=\"VAR\" id=\";%_Xz2^W|D@`pj|28,!5\" variabletype=\"float\">Temperatur</field>\n </block>\n </value>\n </block>\n </next>\n </block>\n </statement>\n <next>\n <block type=\"sensebox_display_clearDisplay\" id=\"E%fqa!5WcG$MDFM..7)G\"></block>\n </next>\n </block>\n </next>\n </block>\n </next>\n </block>\n </statement>\n </block>\n</xml>"
}
]
}

View File

@ -1,41 +1,41 @@
import { TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE, TUTORIAL_XML, TUTORIAL_ID, TUTORIAL_STEP } from '../actions/types';
import tutorials from '../data/tutorials.json';
import tutorials from '../data/tutorials';
const initialStatus = () => {
if(window.localStorage.getItem('status')){
if (window.localStorage.getItem('status')) {
var status = JSON.parse(window.localStorage.getItem('status'));
var existingTutorialIds = tutorials.map((tutorial, i) => {
var tutorialsId = tutorial.id;
var statusIndex = status.findIndex(status => status.id === tutorialsId);
if(statusIndex > -1){
if (statusIndex > -1) {
var tasks = tutorial.steps.filter(step => step.type === 'task');
var existingTaskIds = tasks.map((task, j) => {
var tasksId = task.id;
if(status[statusIndex].tasks.findIndex(task => task.id === tasksId) === -1){
if (status[statusIndex].tasks.findIndex(task => task.id === tasksId) === -1) {
// task does not exist
status[statusIndex].tasks.push({id: tasksId});
status[statusIndex].tasks.push({ id: tasksId });
}
return tasksId;
});
// deleting old tasks which do not longer exist
if(existingTaskIds.length > 0){
if (existingTaskIds.length > 0) {
status[statusIndex].tasks = status[statusIndex].tasks.filter(task => existingTaskIds.indexOf(task.id) > -1);
}
}
else{
status.push({id: tutorialsId, tasks: tutorial.steps.filter(step => step.type === 'task').map(task => {return {id: task.id};})});
else {
status.push({ id: tutorialsId, tasks: tutorial.steps.filter(step => step.type === 'task').map(task => { return { id: task.id }; }) });
}
return tutorialsId;
});
// deleting old tutorials which do not longer exist
if(existingTutorialIds.length > 0){
if (existingTutorialIds.length > 0) {
status = status.filter(status => existingTutorialIds.indexOf(status.id) > -1);
}
return status;
}
// window.localStorage.getItem('status') does not exist
return tutorials.map(tutorial => {return {id: tutorial.id, tasks: tutorial.steps.filter(step => step.type === 'task').map(task => {return {id: task.id};})};});
return tutorials.map(tutorial => { return { id: tutorial.id, tasks: tutorial.steps.filter(step => step.type === 'task').map(task => { return { id: task.id }; }) }; });
};
const initialState = {
@ -45,8 +45,8 @@ const initialState = {
change: 0
};
export default function(state = initialState, action){
switch(action.type){
export default function (state = initialState, action) {
switch (action.type) {
case TUTORIAL_SUCCESS:
case TUTORIAL_ERROR:
case TUTORIAL_XML: