Merge branch 'development' into feat/senseBoxConnect

This commit is contained in:
Mario Pesch 2021-11-23 11:55:15 +01:00 committed by GitHub
commit d167d883b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 11231 additions and 8820 deletions

26
CITATION.cff Normal file
View File

@ -0,0 +1,26 @@
# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!
cff-version: 1.2.0
title: senseBox Learn- and Programming Environment
message: Please cite this software using these metadata.
type: software
version: 1.0.0
date-released: 2021-09-30
url: "https://github.com/sensebox/React-Ardublockly"
authors:
- given-names: Mario
family-names: Pesch
email: mario.pesch@uni-muenster.de
affiliation: >-
Institute for geoinformatics University of
Muenster
- given-names: Luc
family-names: Niski
affiliation: >-
Institute for geoinformatics University of
Muenster
- given-names: Felix
family-names: Erdmann
email: f.erdmann@reedu.de
affiliation: Reedu GmbH & Co. KG

18
package-lock.json generated
View File

@ -22,7 +22,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.21.2",
"axios": "^0.22.0",
"blockly": "^6.20210701.0",
"file-saver": "^2.0.2",
"mnemonic-id": "^3.2.7",
@ -5152,11 +5152,11 @@
}
},
"node_modules/axios": {
"version": "0.21.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz",
"integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==",
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.22.0.tgz",
"integrity": "sha512-Z0U3uhqQeg1oNcihswf4ZD57O3NrR1+ZXhxaROaWpDmsDTx7T2HNBV2ulBtie2hwJptu8UvgnJoK+BIqdzh/1w==",
"dependencies": {
"follow-redirects": "^1.14.0"
"follow-redirects": "^1.14.4"
}
},
"node_modules/axobject-query": {
@ -29080,11 +29080,11 @@
"integrity": "sha512-vwPpH4Aj4122EW38mxO/fxhGKtwWTMLDIJfZ1He0Edbtjcfna/R3YB67yVhezUMzqc3Jr3+Ii50KRntlENL4xQ=="
},
"axios": {
"version": "0.21.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz",
"integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==",
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.22.0.tgz",
"integrity": "sha512-Z0U3uhqQeg1oNcihswf4ZD57O3NrR1+ZXhxaROaWpDmsDTx7T2HNBV2ulBtie2hwJptu8UvgnJoK+BIqdzh/1w==",
"requires": {
"follow-redirects": "^1.14.0"
"follow-redirects": "^1.14.4"
}
},
"axobject-query": {

View File

@ -17,7 +17,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.21.2",
"axios": "^0.22.0",
"blockly": "^6.20210701.0",
"file-saver": "^2.0.2",
"mnemonic-id": "^3.2.7",

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@ -11,6 +11,7 @@ import "./blocks/index";
import "./generator/index";
import { ZoomToFitControl } from "@blockly/zoom-to-fit";
import { initialXml } from "./initialXml.js";
import { getMaxInstances } from "./helpers/maxInstances";
class BlocklyWindow extends Component {
constructor(props) {
@ -24,6 +25,7 @@ class BlocklyWindow extends Component {
this.props.clearStats();
workspace.addChangeListener((event) => {
this.props.onChangeWorkspace(event);
// switch on that a block is displayed disabled or not depending on whether it is correctly connected
// for SVG display, a deactivated block in the display is undesirable
if (this.props.blockDisabled) {
@ -37,6 +39,7 @@ class BlocklyWindow extends Component {
componentDidUpdate(props) {
const workspace = Blockly.getMainWorkspace();
var xml = this.props.initialXml;
// if svg is true, then the update process is done in the BlocklySvg component
if (props.initialXml !== xml && !this.props.svg) {
@ -69,6 +72,7 @@ class BlocklyWindow extends Component {
this.props.trashcan !== undefined ? this.props.trashcan : true
}
renderer={this.props.renderer}
maxInstances={getMaxInstances()}
zoom={{
// https://developers.google.com/blockly/guides/configure/web/zoom
controls:

File diff suppressed because it is too large Load Diff

View File

@ -1,304 +1,329 @@
import * as Blockly from 'blockly/core';
import { getColour } from '../helpers/colour';
import * as Types from '../helpers/types'
import { FieldSlider } from '@blockly/field-slider';
import * as Blockly from "blockly/core";
import { getColour } from "../helpers/colour";
import * as Types from "../helpers/types";
import { FieldSlider } from "@blockly/field-slider";
Blockly.Blocks['sensebox_display_beginDisplay'] = {
init: function () {
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_beginDisplay)
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_display_beginDisplay_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
}
Blockly.Blocks["sensebox_display_beginDisplay"] = {
init: function () {
this.appendDummyInput().appendField(
Blockly.Msg.senseBox_display_beginDisplay
);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_display_beginDisplay_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
},
};
Blockly.Blocks['sensebox_display_clearDisplay'] = {
init: function () {
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_clearDisplay)
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_display_clearDisplay_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
}
Blockly.Blocks["sensebox_display_clearDisplay"] = {
init: function () {
this.appendDummyInput().appendField(
Blockly.Msg.senseBox_display_clearDisplay
);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_display_clearDisplay_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
},
};
Blockly.Blocks['sensebox_display_printDisplay'] = {
init: function (block) {
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_printDisplay);
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_color)
.appendField(new Blockly.FieldDropdown([[Blockly.Msg.senseBox_display_white, "WHITE,BLACK"], [Blockly.Msg.senseBox_display_black, "BLACK,WHITE"]]), "COLOR");
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_setSize)
.appendField(new FieldSlider(1, 1, 4), "SIZE");
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_printDisplay_x)
.appendField(new FieldSlider(0, 0, 64), "X");
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_printDisplay_y)
.appendField(new FieldSlider(0, 0, 128), "Y");
this.appendValueInput('printDisplay')
.appendField(Blockly.Msg.senseBox_display_printDisplay_value)
.setCheck(null);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.senseBox_display_printDisplay_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
},
/**
* Called whenever anything on the workspace changes.
* Add warning if block is not nested inside a the correct loop.
* @param {!Blockly.Events.Abstract} e Change event.
* @this Blockly.Block
*/
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ['sensebox_display_show'],
};
Blockly.Blocks['sensebox_display_fastPrint'] = {
init: function (block) {
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_fastPrint_show);
this.appendValueInput("Title1", 'Title1')
.appendField(Blockly.Msg.senseBox_display_fastPrint_title);
this.appendValueInput("Value1", 'Value1')
.appendField(Blockly.Msg.senseBox_display_fastPrint_value);
this.appendValueInput("Dimension1", 'Dimension1')
.appendField(Blockly.Msg.senseBox_display_fastPrint_dimension);
this.appendValueInput("Title2", 'Title2')
.appendField(Blockly.Msg.senseBox_display_fastPrint_title);
this.appendValueInput("Value2", 'Value2')
.appendField(Blockly.Msg.senseBox_display_fastPrint_value);
this.appendValueInput("Dimension2", 'Dimension2')
.appendField(Blockly.Msg.senseBox_display_fastPrint_dimension);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.sensebox_display_fastPrint_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
},
/**
Blockly.Blocks["sensebox_display_printDisplay"] = {
init: function (block) {
this.setColour(getColour().sensebox);
this.appendDummyInput().appendField(
Blockly.Msg.senseBox_display_printDisplay
);
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_color)
.appendField(
new Blockly.FieldDropdown([
[Blockly.Msg.senseBox_display_white, "WHITE,BLACK"],
[Blockly.Msg.senseBox_display_black, "BLACK,WHITE"],
]),
"COLOR"
);
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_setSize)
.appendField(new FieldSlider(1, 1, 4), "SIZE");
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_printDisplay_x)
.appendField(new FieldSlider(0, 0, 128), "X");
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_printDisplay_y)
.appendField(new FieldSlider(0, 0, 64), "Y");
this.appendValueInput("printDisplay")
.appendField(Blockly.Msg.senseBox_display_printDisplay_value)
.setCheck(null);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.senseBox_display_printDisplay_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
},
/**
* Called whenever anything on the workspace changes.
* Add warning if block is not nested inside a the correct loop.
* @param {!Blockly.Events.Abstract} e Change event.
* @this Blockly.Block
*/
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ['sensebox_display_show'],
};
Blockly.Blocks['sensebox_display_plotDisplay'] = {
init: function () {
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_display_plotDisplay)
this.appendValueInput("Title", 'Text')
.appendField(Blockly.Msg.senseBox_display_plotTitle);
this.appendValueInput("YLabel", 'Text')
.appendField(Blockly.Msg.senseBox_display_plotYLabel);
this.appendValueInput("XLabel", 'Text')
.appendField(Blockly.Msg.senseBox_display_plotXLabel);
this.appendValueInput("XRange1", 'Number')
.appendField(Blockly.Msg.senseBox_display_plotXRange1);
this.appendValueInput("XRange2", 'Number')
.appendField(Blockly.Msg.senseBox_display_plotXRange2)
this.appendValueInput("YRange1", 'Number')
.appendField(Blockly.Msg.senseBox_display_plotYRange1);
this.appendValueInput("YRange2", 'Number')
.appendField(Blockly.Msg.senseBox_display_plotYRange2);
this.setInputsInline(false);
this.appendValueInput("XTick", 'Number')
.appendField(Blockly.Msg.senseBox_display_plotXTick);
this.appendValueInput("YTick", 'Number')
.appendField(Blockly.Msg.senseBox_display_plotYTick);
this.appendValueInput("TimeFrame", 'Number')
.appendField(Blockly.Msg.senseBox_display_plotTimeFrame);
this.appendValueInput('plotDisplay')
.appendField(Blockly.Msg.senseBox_display_printDisplay_value)
.setCheck(null);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.senseBox_display_printDisplay_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
},
/**
* Called whenever anything on the workspace changes.
* Add warning if block is not nested inside a the correct loop.
* @param {!Blockly.Events.Abstract} e Change event.
* @this Blockly.Block
*/
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ['sensebox_display_show'],
};
Blockly.Blocks['sensebox_display_show'] = {
init: function () {
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_display_show);
this.appendStatementInput('SHOW');
this.setTooltip(Blockly.Msg.sensebox_display_show_tip);
this.setHelpUrl('');
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ["sensebox_display_show"],
};
Blockly.Blocks['sensebox_display_fillCircle'] = {
init: function () {
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_display_fillCircle);
this.appendValueInput('X')
.appendField(Blockly.Msg.senseBox_display_printDisplay_x)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput('Y')
.appendField(Blockly.Msg.senseBox_display_printDisplay_y)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput('Radius')
.appendField(Blockly.Msg.sensebox_display_fillCircle_radius)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendDummyInput('fill')
.appendField(Blockly.Msg.senseBox_display_filled)
.appendField(new Blockly.FieldCheckbox("TRUE"), "FILL");
this.setInputsInline(false);
this.setTooltip(Blockly.Msg.senseBox_display_fillCircle_tooltip)
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl)
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
},
/**
Blockly.Blocks["sensebox_display_fastPrint"] = {
init: function (block) {
this.setColour(getColour().sensebox);
this.appendDummyInput().appendField(
Blockly.Msg.senseBox_display_fastPrint_show
);
this.appendValueInput("Title1", "Title1").appendField(
Blockly.Msg.senseBox_display_fastPrint_title
);
this.appendValueInput("Value1", "Value1").appendField(
Blockly.Msg.senseBox_display_fastPrint_value
);
this.appendValueInput("Dimension1", "Dimension1").appendField(
Blockly.Msg.senseBox_display_fastPrint_dimension
);
this.appendValueInput("Title2", "Title2").appendField(
Blockly.Msg.senseBox_display_fastPrint_title
);
this.appendValueInput("Value2", "Value2").appendField(
Blockly.Msg.senseBox_display_fastPrint_value
);
this.appendValueInput("Dimension2", "Dimension2").appendField(
Blockly.Msg.senseBox_display_fastPrint_dimension
);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.sensebox_display_fastPrint_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
},
/**
* Called whenever anything on the workspace changes.
* Add warning if block is not nested inside a the correct loop.
* @param {!Blockly.Events.Abstract} e Change event.
* @this Blockly.Block
*/
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ['sensebox_display_show'],
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ["sensebox_display_show"],
};
Blockly.Blocks['sensebox_display_drawRectangle'] = {
init: function () {
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_display_drawRectangle);
this.appendValueInput('X')
.appendField(Blockly.Msg.senseBox_display_printDisplay_x)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput('Y')
.appendField(Blockly.Msg.senseBox_display_printDisplay_y)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput('width')
.appendField(Blockly.Msg.sensebox_display_drawRectangle_width)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput('height')
.appendField(Blockly.Msg.sensebox_display_drawRectangle_height)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendDummyInput('fill')
.appendField(Blockly.Msg.senseBox_display_filled)
.appendField(new Blockly.FieldCheckbox("TRUE"), "FILL");
this.setInputsInline(false);
this.setTooltip(Blockly.Msg.senseBox_display_drawRectangle_tooltip)
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl)
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
},
/**
Blockly.Blocks["sensebox_display_plotDisplay"] = {
init: function () {
this.setColour(getColour().sensebox);
this.appendDummyInput().appendField(
Blockly.Msg.senseBox_display_plotDisplay
);
this.appendValueInput("Title", "Text").appendField(
Blockly.Msg.senseBox_display_plotTitle
);
this.appendValueInput("YLabel", "Text").appendField(
Blockly.Msg.senseBox_display_plotYLabel
);
this.appendValueInput("XLabel", "Text").appendField(
Blockly.Msg.senseBox_display_plotXLabel
);
this.appendValueInput("XRange1", "Number").appendField(
Blockly.Msg.senseBox_display_plotXRange1
);
this.appendValueInput("XRange2", "Number").appendField(
Blockly.Msg.senseBox_display_plotXRange2
);
this.appendValueInput("YRange1", "Number").appendField(
Blockly.Msg.senseBox_display_plotYRange1
);
this.appendValueInput("YRange2", "Number").appendField(
Blockly.Msg.senseBox_display_plotYRange2
);
this.setInputsInline(false);
this.appendValueInput("XTick", "Number").appendField(
Blockly.Msg.senseBox_display_plotXTick
);
this.appendValueInput("YTick", "Number").appendField(
Blockly.Msg.senseBox_display_plotYTick
);
this.appendValueInput("TimeFrame", "Number").appendField(
Blockly.Msg.senseBox_display_plotTimeFrame
);
this.appendValueInput("plotDisplay")
.appendField(Blockly.Msg.senseBox_display_printDisplay_value)
.setCheck(null);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.senseBox_display_printDisplay_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
},
/**
* Called whenever anything on the workspace changes.
* Add warning if block is not nested inside a the correct loop.
* @param {!Blockly.Events.Abstract} e Change event.
* @this Blockly.Block
*/
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ['sensebox_display_show'],
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ["sensebox_display_show"],
};
Blockly.Blocks["sensebox_display_show"] = {
init: function () {
this.setColour(getColour().sensebox);
this.appendDummyInput().appendField(Blockly.Msg.sensebox_display_show);
this.appendStatementInput("SHOW");
this.setTooltip(Blockly.Msg.sensebox_display_show_tip);
this.setHelpUrl("");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
},
};
Blockly.Blocks["sensebox_display_fillCircle"] = {
init: function () {
this.setColour(getColour().sensebox);
this.appendDummyInput().appendField(
Blockly.Msg.sensebox_display_fillCircle
);
this.appendValueInput("X")
.appendField(Blockly.Msg.senseBox_display_printDisplay_x)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput("Y")
.appendField(Blockly.Msg.senseBox_display_printDisplay_y)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput("Radius")
.appendField(Blockly.Msg.sensebox_display_fillCircle_radius)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendDummyInput("fill")
.appendField(Blockly.Msg.senseBox_display_filled)
.appendField(new Blockly.FieldCheckbox("TRUE"), "FILL");
this.setInputsInline(false);
this.setTooltip(Blockly.Msg.senseBox_display_fillCircle_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
},
/**
* Called whenever anything on the workspace changes.
* Add warning if block is not nested inside a the correct loop.
* @param {!Blockly.Events.Abstract} e Change event.
* @this Blockly.Block
*/
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ["sensebox_display_show"],
};
Blockly.Blocks["sensebox_display_drawRectangle"] = {
init: function () {
this.setColour(getColour().sensebox);
this.appendDummyInput().appendField(
Blockly.Msg.sensebox_display_drawRectangle
);
this.appendValueInput("X")
.appendField(Blockly.Msg.senseBox_display_printDisplay_x)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput("Y")
.appendField(Blockly.Msg.senseBox_display_printDisplay_y)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput("width")
.appendField(Blockly.Msg.sensebox_display_drawRectangle_width)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendValueInput("height")
.appendField(Blockly.Msg.sensebox_display_drawRectangle_height)
.setCheck(Types.NUMBER.compatibleTypes);
this.appendDummyInput("fill")
.appendField(Blockly.Msg.senseBox_display_filled)
.appendField(new Blockly.FieldCheckbox("TRUE"), "FILL");
this.setInputsInline(false);
this.setTooltip(Blockly.Msg.senseBox_display_drawRectangle_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_display_helpurl);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
},
/**
* Called whenever anything on the workspace changes.
* Add warning if block is not nested inside a the correct loop.
* @param {!Blockly.Events.Abstract} e Change event.
* @this Blockly.Block
*/
onchange: function (e) {
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
}
},
LOOP_TYPES: ["sensebox_display_show"],
};

View File

@ -173,7 +173,7 @@ void writeMeasurementsToSdCard(char* timeStamp, uint32_t latitudes, uint32_t lon
dtostrf(latitude, 1, 7, lat);
sprintf_P(buffer, PSTR("%s,%9.2f,%s,%02s,%02s"), measurements[i].sensorId, measurements[i].value, timeStamp, lng, lat);
// transmit buffer to client
${filename}.print(buffer);
${filename}.println(buffer);
}
// reset num_measurements
num_measurements = 0;

View File

@ -3,42 +3,129 @@
*
*/
const sensebox_mcu = {
description: 'senseBox Microcontroller Unit based on Microchip SAMD21G18A',
compilerFlag: 'arduino:samd',
digitalPins: [['D1', '1'], ['D2', '2'], ['D3', '3'], ['D4', '4'], ['D5', '5'], ['D6', '6']],
digitalPinsLED: [['BUILTIN_1', '7'], ['BUILTIN_2', '8'], ['D1', '1'], ['D2', '2'], ['D3', '3'], ['D4', '4'], ['D5', '5'], ['D6', '6']],
digitalPinsButton: [['on Board', '0'], ['D1', '1'], ['D2', '2'], ['D3', '3'], ['D4', '4'], ['D5', '5'], ['D6', '6']],
pwmPins: [['D1', '1'], ['D2', '2'], ['D3', '3'], ['D4', '4'], ['D5', '5'], ['D6', '6']],
serial: [['serial', 'SerialUSB'], ['serial_1', 'Serial1'], ['serial_2', 'Serial2']],
serialPins: {
SerialUSB: [['RX', ''], ['TX', '']],
Serial1: [['RX', '11'], ['TX', '10']],
Serial2: [['RX', '13'], ['TX', '12']]
},
serialSpeed: [['300', '300'], ['600', '600'], ['1200', '1200'],
['2400', '2400'], ['4800', '4800'], ['9600', '9600'],
['14400', '14400'], ['19200', '19200'], ['28800', '28800'],
['31250', '31250'], ['38400', '38400'], ['57600', '57600'],
['115200', '115200']],
spi: [['SPI', 'SPI']],
spiPins: { SPI: [['MOSI', '19'], ['MISO', '21'], ['SCK', '20']] },
spiClockDivide: [['2 (8MHz)', 'SPI_CLOCK_DIV2'],
['4 (4MHz)', 'SPI_CLOCK_DIV4'],
['8 (2MHz)', 'SPI_CLOCK_DIV8'],
['16 (1MHz)', 'SPI_CLOCK_DIV16'],
['32 (500KHz)', 'SPI_CLOCK_DIV32'],
['64 (250KHz)', 'SPI_CLOCK_DIV64'],
['128 (125KHz)', 'SPI_CLOCK_DIV128']],
i2c: [['I2C', 'Wire']],
i2cPins: { Wire: [['SDA', '17'], ['SCL', '16']] },
i2cSpeed: [['100kHz', '100000L'], ['400kHz', '400000L']],
builtinLed: [['BUILTIN_1', '7'], ['BUILTIN_2', '8']],
interrupt: [['interrupt1', '1'], ['interrupt2', '2'], ['interrupt3', '3'], ['interrupt4', '4'], ['interrupt5', '5'], ['interrupt6', '6']],
analogPins: [['A1', 'A1'], ['A2', 'A2'], ['A3', 'A3'], ['A4', 'A4'], ['A5', 'A5'], ['A6', 'A6']],
serial_baud_rate: 9600,
parseKey: '_*_'
description: "senseBox Microcontroller Unit based on Microchip SAMD21G18A",
compilerFlag: "arduino:samd",
digitalPins: [
["A1", "1"],
["A2", "2"],
["B3", "3"],
["B4", "4"],
["C5", "5"],
["C6", "6"],
],
digitalPinsLED: [
["BUILTIN_1", "7"],
["BUILTIN_2", "8"],
["A1", "1"],
["A2", "2"],
["B3", "3"],
["B4", "4"],
["C5", "5"],
["C6", "6"],
],
digitalPinsButton: [
["on Board", "0"],
["A1", "1"],
["A2", "2"],
["B3", "3"],
["B4", "4"],
["C5", "5"],
["C6", "6"],
],
pwmPins: [
["A1", "1"],
["A2", "2"],
["B3", "3"],
["B4", "4"],
["C5", "5"],
["C6", "6"],
],
serial: [
["serial", "SerialUSB"],
["serial_1", "Serial1"],
["serial_2", "Serial2"],
],
serialPins: {
SerialUSB: [
["RX", ""],
["TX", ""],
],
Serial1: [
["RX", "11"],
["TX", "10"],
],
Serial2: [
["RX", "13"],
["TX", "12"],
],
},
serialSpeed: [
["300", "300"],
["600", "600"],
["1200", "1200"],
["2400", "2400"],
["4800", "4800"],
["9600", "9600"],
["14400", "14400"],
["19200", "19200"],
["28800", "28800"],
["31250", "31250"],
["38400", "38400"],
["57600", "57600"],
["115200", "115200"],
],
spi: [["SPI", "SPI"]],
spiPins: {
SPI: [
["MOSI", "19"],
["MISO", "21"],
["SCK", "20"],
],
},
spiClockDivide: [
["2 (8MHz)", "SPI_CLOCK_DIV2"],
["4 (4MHz)", "SPI_CLOCK_DIV4"],
["8 (2MHz)", "SPI_CLOCK_DIV8"],
["16 (1MHz)", "SPI_CLOCK_DIV16"],
["32 (500KHz)", "SPI_CLOCK_DIV32"],
["64 (250KHz)", "SPI_CLOCK_DIV64"],
["128 (125KHz)", "SPI_CLOCK_DIV128"],
],
i2c: [["I2C", "Wire"]],
i2cPins: {
Wire: [
["SDA", "17"],
["SCL", "16"],
],
},
i2cSpeed: [
["100kHz", "100000L"],
["400kHz", "400000L"],
],
builtinLed: [
["BUILTIN_1", "7"],
["BUILTIN_2", "8"],
],
interrupt: [
["interrupt1", "1"],
["interrupt2", "2"],
["interrupt3", "3"],
["interrupt4", "4"],
["interrupt5", "5"],
["interrupt6", "6"],
],
analogPins: [
["A1", "A1"],
["A2", "A2"],
["B3", "A3"],
["B4", "A4"],
["C5", "A5"],
["C6", "A6"],
],
serial_baud_rate: 9600,
parseKey: "_*_",
};
export const selectedBoard = () => {
return sensebox_mcu;
return sensebox_mcu;
};

View File

@ -0,0 +1,21 @@
/**
* To limit number of specific blocks in the workspace add block name and number of maxInstances here.
*
*/
const maxInstances = {
sensebox_wifi: 1,
sensebox_startap: 1,
sensebox_display_beginDisplay: 1,
sensebox_telegram: 1,
sensebox_telegram_do: 1,
sensebox_interval_timer: 1,
sensebox_osem_connection: 1,
sensebox_lora_initialize_otaa: 1,
sensebox_lora_initialize_abp: 1,
sensebox_phyphox_init: 1,
};
export const getMaxInstances = () => {
return maxInstances;
};

View File

@ -1,10 +1,10 @@
export const FAQ = {
/**
* FAQ
*/
/**
* FAQ
*/
faq_q1_question: `Wie kann ich mein Programm auf die senseBox kopieren?`,
faq_q1_answer: `Um Programme auf die senseBox zu kopieren wird diese mit dem Micro USB Kabel an den Computer angeschlossen. Mache anschließend auf der senseBox MCU einen Doppelklick auf den roten Reset Button. Die senseBox wird nun als Wechseldatenträger an deinem Computer erkannt und die zuvor erstellen Programm können per Drag & Drop kopiert werden. Nach jeder Änderung des Programmcodes muss das Programm neu kompiliert und übertragen werden
faq_q1_question: `Wie kann ich mein Programm auf die senseBox kopieren?`,
faq_q1_answer: `Um Programme auf die senseBox zu kopieren wird diese mit dem Micro USB Kabel an den Computer angeschlossen. Mache anschließend auf der senseBox MCU einen Doppelklick auf den roten Reset Button. Die senseBox wird nun als Wechseldatenträger an deinem Computer erkannt und die zuvor erstellen Programm können per Drag & Drop kopiert werden. Nach jeder Änderung des Programmcodes muss das Programm neu kompiliert und übertragen werden
#### Lernmodus der MCU aktivieren
<iframe width="560" height="315" src="https://www.youtube.com/embed/jzlOJ7Zuqqw" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
@ -14,14 +14,16 @@ Das Kopieren der Programme unter MacOS funktioniert nicht über den Finder, es g
- [muCommander](https://www.mucommander.com/)
`,
faq_q2_question: `Mit welcher senseBox ist die Programmierumgebung kompatibel?`,
faq_q2_answer: `
faq_q2_question: `Mit welcher senseBox ist die Programmierumgebung kompatibel?`,
faq_q2_answer: `
Grundsätzlich kann die Programmierumgebung mit jeder senseBox mit senseBox MCU verwendet werden.
`,
faq_q3_question: `Ich habe einen Fehler gefunden oder etwas funktioniert nicht. Wo kann ich diesen melden?`,
faq_q3_answer: `
faq_q3_question: `Ich habe einen Fehler gefunden oder etwas funktioniert nicht. Wo kann ich diesen melden?`,
faq_q3_answer: `
Am besten legst du dazu ein Issue auf [Github](https://github.com/sensebox/React-Ardublockly/issues) an. Alternativ kannst du uns auch eine Email an info(at)sensebox.de senden
`,
}
faq_tablet_question: `Kann ich die senseBox auch über ein Tablet programmieren?`,
faq_tablet_answer: `Ja! Installiere dazu die senseBox Connect App aus dem App Store und rufe die Learn- und Programmierumgebung in deinem Browser am Tablet auf. Genaue Informationen zur Verwendung der App findest du unter [https://sensebox.de/app](https://sensebox.de/app)`,
};

View File

@ -1,50 +1,64 @@
export const LOGIC = {
CONTROLS_IF_ELSEIF_TOOLTIP: "Eine weitere Bedingung hinzufügen.",
CONTROLS_IF_ELSE_TOOLTIP:
"Eine sonst-Bedingung hinzufügen, führt eine Anweisung aus, falls keine Bedingung zutrifft.",
CONTROLS_IF_HELPURL: "https://github.com/google/blockly/wiki/IfElse", // untranslated
CONTROLS_IF_IF_TOOLTIP: "Hinzufügen, entfernen oder sortieren von Sektionen",
CONTROLS_IF_MSG_ELSE: "sonst",
CONTROLS_IF_MSG_ELSEIF: "sonst wenn",
CONTROLS_IF_MSG_IF: "wenn",
CONTROLS_IF_TOOLTIP_1:
"Wenn eine Bedingung wahr (true) ist, dann führe eine Anweisung aus.",
CONTROLS_IF_TOOLTIP_2:
"Wenn eine Bedingung wahr (true) ist, dann führe die erste Anweisung aus. Ansonsten führe die zweite Anweisung aus.",
CONTROLS_IF_TOOLTIP_3:
"Wenn die erste Bedingung wahr (true) ist, dann führe die erste Anweisung aus. Oder wenn die zweite Bedingung wahr (true) ist, dann führe die zweite Anweisung aus.",
CONTROLS_IF_TOOLTIP_4:
"Wenn die erste Bedingung wahr (true) ist, dann führe die erste Anweisung aus. Oder wenn die zweite Bedingung wahr (true) ist, dann führe die zweite Anweisung aus. Falls keine der beiden Bedingungen wahr (true) ist, dann führe die dritte Anweisung aus.",
LOGIC_BOOLEAN_HELPURL: "https://github.com/google/blockly/wiki/Logic#values", // untranslated
LOGIC_BOOLEAN_TOOLTIP: "Ist entweder wahr (true) oder falsch (false)",
LOGIC_BOOLEAN_TRUE: "wahr",
LOGIC_COMPARE_HELPURL: "https://de.wikipedia.org/wiki/Vergleich_%28Zahlen%29",
LOGIC_COMPARE_TOOLTIP_EQ: "Ist wahr (true), wenn beide Werte gleich sind.",
LOGIC_COMPARE_TOOLTIP_GT:
"Ist wahr (true), wenn der erste Wert größer als der zweite Wert ist.",
LOGIC_COMPARE_TOOLTIP_GTE:
"Ist wahr (true), wenn der erste Wert größer als oder gleich groß wie der zweite Wert ist.",
LOGIC_COMPARE_TOOLTIP_LT:
"Ist wahr (true), wenn der erste Wert kleiner als der zweite Wert ist.",
LOGIC_COMPARE_TOOLTIP_LTE:
"Ist wahr (true), wenn der erste Wert kleiner als oder gleich groß wie der zweite Wert ist.",
LOGIC_COMPARE_TOOLTIP_NEQ:
"Ist wahr (true), wenn beide Werte unterschiedlich sind.",
LOGIC_NEGATE_HELPURL: "https://github.com/google/blockly/wiki/Logic#not", // untranslated
LOGIC_NEGATE_TITLE: "nicht %1",
LOGIC_NEGATE_TOOLTIP:
"Ist wahr (true), wenn der Eingabewert falsch (false) ist. Ist falsch (false), wenn der Eingabewert wahr (true) ist.",
LOGIC_NULL: "null",
LOGIC_NULL_HELPURL: "https://de.wikipedia.org/wiki/Nullwert",
LOGIC_NULL_TOOLTIP: "Ist NULL.",
LOGIC_OPERATION_AND: "und",
LOGIC_OPERATION_HELPURL:
"https://github.com/google/blockly/wiki/Logic#logical-operations", // untranslated
LOGIC_OPERATION_OR: "oder",
LOGIC_OPERATION_TOOLTIP_AND:
"Ist wahr (true), wenn beide Werte wahr (true) sind.",
LOGIC_OPERATION_TOOLTIP_OR:
"Ist wahr (true), wenn einer der beiden Werte wahr (true) ist.",
LOGIC_TERNARY_CONDITION: "teste",
LOGIC_TERNARY_HELPURL: "https://de.wikipedia.org/wiki/%3F:#Auswahloperator",
LOGIC_TERNARY_IF_FALSE: "wenn falsch",
LOGIC_TERNARY_IF_TRUE: "wenn wahr",
LOGIC_TERNARY_TOOLTIP:
'Überprüft eine Bedingung "teste". Wenn die Bedingung wahr ist, wird der "wenn wahr" Wert zurückgegeben, andernfalls der "wenn falsch" Wert',
CONTROLS_IF_ELSEIF_TOOLTIP: "Eine weitere Bedingung hinzufügen.",
CONTROLS_IF_ELSE_TOOLTIP: "Eine sonst-Bedingung hinzufügen, führt eine Anweisung aus, falls keine Bedingung zutrifft.",
CONTROLS_IF_HELPURL: "https://github.com/google/blockly/wiki/IfElse", // untranslated
CONTROLS_IF_IF_TOOLTIP: "Hinzufügen, entfernen oder sortieren von Sektionen",
CONTROLS_IF_MSG_ELSE: "sonst",
CONTROLS_IF_MSG_ELSEIF: "sonst wenn",
CONTROLS_IF_MSG_IF: "wenn",
CONTROLS_IF_TOOLTIP_1: "Wenn eine Bedingung wahr (true) ist, dann führe eine Anweisung aus.",
CONTROLS_IF_TOOLTIP_2: "Wenn eine Bedingung wahr (true) ist, dann führe die erste Anweisung aus. Ansonsten führe die zweite Anweisung aus.",
CONTROLS_IF_TOOLTIP_3: "Wenn die erste Bedingung wahr (true) ist, dann führe die erste Anweisung aus. Oder wenn die zweite Bedingung wahr (true) ist, dann führe die zweite Anweisung aus.",
CONTROLS_IF_TOOLTIP_4: "Wenn die erste Bedingung wahr (true) ist, dann führe die erste Anweisung aus. Oder wenn die zweite Bedingung wahr (true) ist, dann führe die zweite Anweisung aus. Falls keine der beiden Bedingungen wahr (true) ist, dann führe die dritte Anweisung aus.",
LOGIC_BOOLEAN_HELPURL: "https://github.com/google/blockly/wiki/Logic#values", // untranslated
LOGIC_BOOLEAN_TOOLTIP: "Ist entweder wahr (true) oder falsch (false)",
LOGIC_BOOLEAN_TRUE: "wahr",
LOGIC_COMPARE_HELPURL: "https://de.wikipedia.org/wiki/Vergleich_%28Zahlen%29",
LOGIC_COMPARE_TOOLTIP_EQ: "Ist wahr (true), wenn beide Werte gleich sind.",
LOGIC_COMPARE_TOOLTIP_GT: "Ist wahr (true), wenn der erste Wert größer als der zweite Wert ist.",
LOGIC_COMPARE_TOOLTIP_GTE: "Ist wahr (true), wenn der erste Wert größer als oder gleich groß wie der zweite Wert ist.",
LOGIC_COMPARE_TOOLTIP_LT: "Ist wahr (true), wenn der erste Wert kleiner als der zweite Wert ist.",
LOGIC_COMPARE_TOOLTIP_LTE: "Ist wahr (true), wenn der erste Wert kleiner als oder gleich groß wie der zweite Wert ist.",
LOGIC_COMPARE_TOOLTIP_NEQ: "Ist wahr (true), wenn beide Werte unterschiedlich sind.",
LOGIC_NEGATE_HELPURL: "https://github.com/google/blockly/wiki/Logic#not", // untranslated
LOGIC_NEGATE_TITLE: "nicht %1",
LOGIC_NEGATE_TOOLTIP: "Ist wahr (true), wenn der Eingabewert falsch (false) ist. Ist falsch (false), wenn der Eingabewert wahr (true) ist.",
LOGIC_NULL: "null",
LOGIC_NULL_HELPURL: "https://de.wikipedia.org/wiki/Nullwert",
LOGIC_NULL_TOOLTIP: "Ist NULL.",
LOGIC_OPERATION_AND: "und",
LOGIC_OPERATION_HELPURL: "https://github.com/google/blockly/wiki/Logic#logical-operations", // untranslated
LOGIC_OPERATION_OR: "oder",
LOGIC_OPERATION_TOOLTIP_AND: "Ist wahr (true), wenn beide Werte wahr (true) sind.",
LOGIC_OPERATION_TOOLTIP_OR: "Ist wahr (true), wenn einer der beiden Werte wahr (true) ist.",
LOGIC_TERNARY_CONDITION: "teste",
LOGIC_TERNARY_HELPURL: "https://de.wikipedia.org/wiki/%3F:#Auswahloperator",
LOGIC_TERNARY_IF_FALSE: "wenn falsch",
LOGIC_TERNARY_IF_TRUE: "wenn wahr",
LOGIC_TERNARY_TOOLTIP: "Überprüft eine Bedingung \"teste\". Wenn die Bedingung wahr ist, wird der \"wenn wahr\" Wert zurückgegeben, andernfalls der \"wenn falsch\" Wert",
/**
* Cases
*/
cases_do: "Führe aus",
cases_condition: "Fall (Variable): ",
cases_switch: "Variable",
cases_add: "Fall",
cases_tooltip: "Führt den entsprechenden Fall aus, wenn die Überprüfung der Variable TRUE ergibt. Über das Zahnrad kannst du weitere Fälle hinzufügen. Über den 'Default' fall kannst du bestimmen, was passieren soll wenn keiner der vorher definierten Fälle eingetreten ist.",
}
/**
* Cases
*/
cases_do: "Führe aus",
cases_condition: "Fall (Variable): ",
cases_switch: "Variable",
cases_add: "Fall",
cases_tooltip:
"Führt den entsprechenden Fall aus, wenn die Überprüfung der Variable TRUE ergibt. Über das Zahnrad kannst du weitere Fälle hinzufügen. Über den 'Default' fall kannst du bestimmen, was passieren soll wenn keiner der vorher definierten Fälle eingetreten ist.",
};

View File

@ -244,7 +244,7 @@ export const TRANSLATIONS = {
senseBox_serial_tip:
"Gibt Messwerte oder Daten auf dem Seriellen Monitor der Arduino IDE aus. Praktisch um ohne Display zu arbeiten",
senseBox_output_timestamp: "Zeitstempel (RFC 3339)",
senseBox_led: "LED an digitalen",
senseBox_led: "LED an",
senseBox_led_tip:
"Einfache LED. Beim Anschluss sollte immer ein Vorwiderstand verwendet werden",
senseBox_piezo: "Piezo an digital",

View File

@ -49,7 +49,14 @@ export const UI = {
tooltip_project_title: "Titel des Projektes",
tooltip_check_solution: "Lösung kontrollieren",
tooltip_copy_code: "Code in die Zwischenablage kopieren",
tooltip_statistics_current: "Anzahl aktueller Blöcke",
tooltip_statistics_new: "Anzahl neuer Blöcke",
tooltip_statistics_changed: "Anzahl veränderter Blöcke",
tooltip_statistics_moved: "Anzahl bewegter Blöcke",
tooltip_statistics_deleted: "Anzahl gelöschter Blöcke",
tooltip_statistics_remaining: "Verbleibende Blöcke",
tooltip_statistics_show: "Statistiken anzeigen",
tooltip_start_tour: "Tour starten",
/**
* Messages
*
@ -190,7 +197,7 @@ export const UI = {
/**
* Tutorials
*/
tutorials_home_head: "Tutorial-Übersicht",
tutorials_assessment_task: "Aufgabe",
tutorials_hardware_head: "Für die Umsetzung benötigst du folgende Hardware:",
tutorials_hardware_moreInformation:
@ -202,7 +209,9 @@ export const UI = {
/**
* Tutorial Builder
*/
builder_createNew: "neues Tutorial erstellen",
builder_changeExisting: "bestehendes Tutorial ändern",
builder_deleteExisting: "bestehendes Tutorial löschen",
builder_solution: "Lösung",
builder_solution_submit: "Lösung einreichen",
builder_example_submit: "Beispiel einreichen",
@ -230,7 +239,7 @@ export const UI = {
navbar_tutorials: "Tutorials",
navbar_tutorialbuilder: "Tutorial erstellen",
navbar_gallery: "Gallerie",
navbar_gallery: "Galerie",
navbar_projects: "Projekte",
navbar_menu: "Menü",

View File

@ -1,11 +1,10 @@
export const FAQ = {
/**
* FAQ
*/
/**
* FAQ
*/
faq_q1_question: `How can I copy my program to the senseBox?`,
faq_q1_answer: `To copy programs to the senseBox, connect it to the computer with the Micro USB cable. Then double click on the red reset button on the senseBox MCU. The senseBox will now be recognized as a removable disk on your computer and the previously created programs can be copied via drag & drop. After each change of the program code the program must be recompiled and transferred again.
faq_q1_question: `How can I copy my program to the senseBox?`,
faq_q1_answer: `To copy programs to the senseBox, connect it to the computer with the Micro USB cable. Then double click on the red reset button on the senseBox MCU. The senseBox will now be recognized as a removable disk on your computer and the previously created programs can be copied via drag & drop. After each change of the program code the program must be recompiled and transferred again.
#### Activate learning mode of the MCU
<iframe width="560" height="315" src="https://www.youtube.com/embed/jzlOJ7Zuqqw" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
@ -15,15 +14,15 @@ Copying programs under MacOS does not work via the Finder, but there are still t
- [muCommander](https://www.mucommander.com/)
`,
faq_q2_question: `With which senseBox is the programming environment compatible?`,
faq_q2_answer: `
faq_q2_question: `With which senseBox is the programming environment compatible?`,
faq_q2_answer: `
Basically the programming environment can be used with any senseBox with senseBox MCU.
`,
faq_q3_question: `I found an error or something is not working. Where can I report it?`,
faq_q3_answer: `
faq_q3_question: `I found an error or something is not working. Where can I report it?`,
faq_q3_answer: `
The best way to do this is to create an issue on [Github](https://github.com/sensebox/React-Ardublockly/issues). Alternatively you can send us an email to info(at)sensebox.de
`,
}
faq_tablet_question: `Can I program the senseBox with a tablet?`,
faq_tablet_answer: `Yes! Install the senseBox Connect app from the App Store and call up the learning and programming environment in your browser on the tablet. Detailed information on how to use the app can be found at [https://sensebox.de/app](https://sensebox.de/app)`,
};

View File

@ -1,45 +1,45 @@
export const LED = {
senseBox_led: "LED connected to",
senseBox_led_tip: "simple LED. Don't forget the resistor",
senseBox_rgb_led: "RGB-LED",
senseBox_rgb_led_tip: "RGB-LED",
/**
* WS2818 RGB LED
*/
senseBox_ws2818_rgb_led: "Set RGB-LED at",
senseBox_ws2818_rgb_led_init: "Initialise RGB LED (WS2818)",
senseBox_ws2818_rgb_led_position: "Position",
senseBox_ws2818_rgb_led_brightness: "Brightness",
senseBox_ws2818_rgb_led_tooltip:
"Change the color of your RGB LED with this block. Link a block for the color. If multiple RGB LEDs are chained together you can use the position to determine which LED is controlled.",
senseBox_ws2818_rgb_led_init_tooltip:
"Connect the RGB LED to one of the three **digital/analog ports**. If multiple RGB LEDs are daisy-chained together you can determine which LED is controlled by position.",
senseBox_ws2818_rgb_led_color: "Color",
senseBox_ws2818_rgb_led_number: "Number",
senseBox_led: "LED connected to digital",
senseBox_led_tip: "simple LED. Don't forget the resistor",
/**
* Color
*/
senseBox_rgb_led: "RGB-LED",
senseBox_rgb_led_tip: "RGB-LED",
/**
* WS2818 RGB LED
*/
senseBox_ws2818_rgb_led: "Set RGB-LED at",
senseBox_ws2818_rgb_led_init: "Initialise RGB LED (WS2818)",
senseBox_ws2818_rgb_led_position: "Position",
senseBox_ws2818_rgb_led_brightness: "Brightness",
senseBox_ws2818_rgb_led_tooltip: "Change the color of your RGB LED with this block. Link a block for the color. If multiple RGB LEDs are chained together you can use the position to determine which LED is controlled.",
senseBox_ws2818_rgb_led_init_tooltip: "Connect the RGB LED to one of the three **digital/analog ports**. If multiple RGB LEDs are daisy-chained together you can determine which LED is controlled by position.",
senseBox_ws2818_rgb_led_color: "Color",
senseBox_ws2818_rgb_led_number: "Number",
/**
* Color
*/
COLOUR_BLEND_COLOUR1: "colour 1",
COLOUR_BLEND_COLOUR2: "colour 2",
COLOUR_BLEND_HELPURL: "http://meyerweb.com/eric/tools/color-blend/",
COLOUR_BLEND_RATIO: "ratio",
COLOUR_BLEND_TITLE: "blend",
COLOUR_BLEND_TOOLTIP: "Blends two colours together with a given ratio (0.0 - 1.0).",
COLOUR_PICKER_HELPURL: "https://en.wikipedia.org/wiki/Color",
COLOUR_PICKER_TOOLTIP: "Choose a colour from the palette.",
COLOUR_RANDOM_HELPURL: "http://randomcolour.com",
COLOUR_RANDOM_TITLE: "random colour",
COLOUR_RANDOM_TOOLTIP: "Choose a colour at random.",
COLOUR_RGB_BLUE: "blue",
COLOUR_RGB_GREEN: "green",
COLOUR_RGB_HELPURL: "http://www.december.com/html/spec/colorper.html",
COLOUR_RGB_RED: "red",
COLOUR_RGB_TITLE: "colour with",
COLOUR_RGB_TOOLTIP: "Create a colour with the specified amount of red, green, and blue. All values must be between 0 and 255.",
}
COLOUR_BLEND_COLOUR1: "colour 1",
COLOUR_BLEND_COLOUR2: "colour 2",
COLOUR_BLEND_HELPURL: "http://meyerweb.com/eric/tools/color-blend/",
COLOUR_BLEND_RATIO: "ratio",
COLOUR_BLEND_TITLE: "blend",
COLOUR_BLEND_TOOLTIP:
"Blends two colours together with a given ratio (0.0 - 1.0).",
COLOUR_PICKER_HELPURL: "https://en.wikipedia.org/wiki/Color",
COLOUR_PICKER_TOOLTIP: "Choose a colour from the palette.",
COLOUR_RANDOM_HELPURL: "http://randomcolour.com",
COLOUR_RANDOM_TITLE: "random colour",
COLOUR_RANDOM_TOOLTIP: "Choose a colour at random.",
COLOUR_RGB_BLUE: "blue",
COLOUR_RGB_GREEN: "green",
COLOUR_RGB_HELPURL: "http://www.december.com/html/spec/colorper.html",
COLOUR_RGB_RED: "red",
COLOUR_RGB_TITLE: "colour with",
COLOUR_RGB_TOOLTIP:
"Create a colour with the specified amount of red, green, and blue. All values must be between 0 and 255.",
};

View File

@ -49,6 +49,14 @@ export const UI = {
tooltip_project_title: "Project title",
tooltip_check_solution: "Check solution",
tooltip_copy_code: "Copy Code to clipboard",
tooltip_statistics_current: "Number of current blocks",
tooltip_statistics_new: "Number of new blocks",
tooltip_statistics_changed: "Number of changed blocks",
tooltip_statistics_moved: "Number of moved blocks",
tooltip_statistics_deleted: "Number of deleted blocks",
tooltip_statistics_remaining: "Remaining blocks",
tooltip_statistics_show: "Show statistics",
tooltip_start_tour: "start Tour",
/**
* Messages
@ -183,7 +191,7 @@ export const UI = {
/**
* Tutorials
*/
tutorials_home_head: "Tutorials",
tutorials_assessment_task: "Task",
tutorials_hardware_head:
"For the implementation you need the following hardware:",
@ -196,7 +204,9 @@ export const UI = {
/**
* Tutorial Builder
*/
uilder_createNew: "create new Tutorial",
builder_changeExisting: "change existing Tutorial",
builder_deleteExisting: "remove existing Tutorial",
builder_solution: "Solution",
builder_solution_submit: "Submit Solution",
builder_example_submit: "Submit example",

View File

@ -12,6 +12,8 @@ import BlocklyWindow from "./Blockly/BlocklyWindow";
import CodeViewer from "./CodeViewer";
import TrashcanButtons from "./Workspace/TrashcanButtons";
import HintTutorialExists from "./Tutorial/HintTutorialExists";
import Snackbar from "./Snackbar";
import Grid from "@material-ui/core/Grid";
import IconButton from "@material-ui/core/IconButton";
@ -136,14 +138,18 @@ class Home extends Component {
style={{ position: "relative" }}
>
<Tooltip
title={this.state.codeOn ? "Code ausblenden" : "Code anzeigen"}
title={
this.state.codeOn
? Blockly.Msg.tooltip_hide_code
: Blockly.Msg.tooltip_show_code
}
>
<IconButton
className={`showCode ${
this.state.codeOn
? this.props.classes.codeOn
: this.props.classes.codeOff
}`}
}}
style={{
width: "40px",
height: "40px",
@ -213,6 +219,7 @@ Home.propTypes = {
message: PropTypes.object.isRequired,
statistics: PropTypes.bool.isRequired,
platform: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({

View File

@ -139,7 +139,7 @@ class Navbar extends Component {
</Link>
) : null}
{isHome ? (
<Tooltip title="Hilfe starten" arrow>
<Tooltip title={Blockly.Msg.tooltip_start_tour} arrow>
<IconButton
color="inherit"
className={`openTour ${this.props.classes.button}`}
@ -153,7 +153,7 @@ class Navbar extends Component {
</Tooltip>
) : null}
{isAssessment ? (
<Tooltip title="Hilfe starten" arrow>
<Tooltip title={Blockly.Msg.tooltip_start_tour} arrow>
<IconButton
color="inherit"
className={`openTour ${this.props.classes.button}`}

View File

@ -39,6 +39,7 @@ import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import * as Blockly from "blockly";
const styles = (theme) => ({
backdrop: {
@ -392,7 +393,7 @@ class Builder extends Component {
style={{ color: "black" }}
value="new"
control={<Radio color="primary" />}
label="neues Tutorial erstellen"
label={Blockly.Msg.builder_createNew}
labelPlacement="end"
/>
{filteredTutorials.length > 0 ? (
@ -402,7 +403,7 @@ class Builder extends Component {
disabled={this.props.index === 0}
value="change"
control={<Radio color="primary" />}
label="bestehendes Tutorial ändern"
label={Blockly.Msg.builder_changeExisting}
labelPlacement="end"
/>
<FormControlLabel
@ -410,7 +411,7 @@ class Builder extends Component {
disabled={this.props.index === 0}
value="delete"
control={<Radio color="primary" />}
label="bestehendes Tutorial löschen"
label={Blockly.Msg.builder_deleteExisting}
labelPlacement="end"
/>
</div>

View File

@ -1,129 +1,224 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getTutorials, resetTutorial, tutorialProgress } from '../../actions/tutorialActions';
import { clearMessages } from '../../actions/messageActions';
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import {
getTutorials,
resetTutorial,
tutorialProgress,
} from "../../actions/tutorialActions";
import { clearMessages } from "../../actions/messageActions";
import clsx from 'clsx';
import clsx from "clsx";
import Breadcrumbs from '../Breadcrumbs';
import Breadcrumbs from "../Breadcrumbs";
import { Link } from 'react-router-dom';
import { Link } from "react-router-dom";
import { fade } from '@material-ui/core/styles/colorManipulator';
import { withStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import { fade } from "@material-ui/core/styles/colorManipulator";
import { withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import * as Blockly from "blockly";
const styles = (theme) => ({
outerDiv: {
position: 'absolute',
right: '-30px',
bottom: '-30px',
width: '160px',
height: '160px',
color: fade(theme.palette.secondary.main, 0.6)
position: "absolute",
right: "-30px",
bottom: "-30px",
width: "160px",
height: "160px",
color: fade(theme.palette.secondary.main, 0.6),
},
outerDivError: {
stroke: fade(theme.palette.error.dark, 0.6),
color: fade(theme.palette.error.dark, 0.6)
color: fade(theme.palette.error.dark, 0.6),
},
outerDivSuccess: {
stroke: fade(theme.palette.primary.main, 0.6),
color: fade(theme.palette.primary.main, 0.6)
color: fade(theme.palette.primary.main, 0.6),
},
outerDivOther: {
stroke: fade(theme.palette.secondary.main, 0.6)
stroke: fade(theme.palette.secondary.main, 0.6),
},
innerDiv: {
width: 'inherit',
height: 'inherit',
display: 'table-cell',
verticalAlign: 'middle',
textAlign: 'center'
}
width: "inherit",
height: "inherit",
display: "table-cell",
verticalAlign: "middle",
textAlign: "center",
},
});
class TutorialHome extends Component {
componentDidMount() {
this.props.tutorialProgress();
// retrieve tutorials only if a potential user is loaded - authentication
// is finished (success or failed)
if(!this.props.progress){
if (!this.props.progress) {
this.props.getTutorials();
}
}
componentDidUpdate(props, state) {
if(props.progress !== this.props.progress && !this.props.progress){
if (props.progress !== this.props.progress && !this.props.progress) {
// authentication is completed
this.props.getTutorials();
}
if(this.props.message.id === 'GET_TUTORIALS_FAIL'){
if (this.props.message.id === "GET_TUTORIALS_FAIL") {
alert(this.props.message.msg);
}
}
componentWillUnmount() {
this.props.resetTutorial();
if(this.props.message.msg){
if (this.props.message.msg) {
this.props.clearMessages();
}
}
render() {
return (
this.props.isLoading ? null :
return this.props.isLoading ? null : (
<div>
<Breadcrumbs content={[{ link: '/tutorial', title: 'Tutorial' }]} />
<Breadcrumbs content={[{ link: "/tutorial", title: "Tutorial" }]} />
<h1>Tutorial-Übersicht</h1>
<h1>{Blockly.Msg.tutorials_home_head}</h1>
<Grid container spacing={2}>
{this.props.tutorials.map((tutorial, i) => {
var status = this.props.status.filter(status => status._id === tutorial._id)[0];
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 tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other';
var error =
status.tasks.filter((task) => task.type === "error").length > 0;
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%' }}>
{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>}
{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>
: null}
<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>
)}
{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>
) : 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={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>
}
{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>
)}
</div>
</div>
</Paper>
</Link>
</Grid>
)
);
})}
</Grid>
</div>
);
};
}
}
TutorialHome.propTypes = {
@ -136,16 +231,21 @@ TutorialHome.propTypes = {
tutorials: PropTypes.array.isRequired,
isLoading: PropTypes.bool.isRequired,
message: PropTypes.object.isRequired,
progress: PropTypes.bool.isRequired
progress: PropTypes.bool.isRequired,
};
const mapStateToProps = state => ({
const mapStateToProps = (state) => ({
change: state.tutorial.change,
status: state.tutorial.status,
tutorials: state.tutorial.tutorials,
isLoading: state.tutorial.progress,
message: state.message,
progress: state.auth.progress
progress: state.auth.progress,
});
export default connect(mapStateToProps, { getTutorials, resetTutorial, clearMessages, tutorialProgress })(withStyles(styles, { withTheme: true })(TutorialHome));
export default connect(mapStateToProps, {
getTutorials,
resetTutorial,
clearMessages,
tutorialProgress,
})(withStyles(styles, { withTheme: true })(TutorialHome));

View File

@ -124,57 +124,59 @@ export class Login extends Component {
type={this.state.type}
key={this.state.key}
/>
<TextField
style={{ marginBottom: "10px" }}
// variant='outlined'
type="text"
label={Blockly.Msg.labels_username}
name="email"
value={this.state.email}
onChange={this.onChange}
fullWidth={true}
/>
<TextField
// variant='outlined'
type={this.state.showPassword ? "text" : "password"}
label={Blockly.Msg.labels_password}
name="password"
value={this.state.password}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={this.handleClickShowPassword}
onMouseDown={this.handleMouseDownPassword}
edge="end"
>
<FontAwesomeIcon
size="xs"
icon={this.state.showPassword ? faEyeSlash : faEye}
/>
</IconButton>
</InputAdornment>
),
}}
onChange={this.onChange}
fullWidth={true}
/>
<p>
<Button
color="primary"
variant="contained"
onClick={this.onSubmit}
style={{ width: "100%" }}
>
{this.props.progress ? (
<div style={{ height: "24.5px" }}>
<CircularProgress color="inherit" size={20} />
</div>
) : (
Blockly.Msg.button_login
)}
</Button>
</p>
<form onSubmit={this.onSubmit}>
<TextField
style={{ marginBottom: "10px" }}
// variant='outlined'
type="text"
label={Blockly.Msg.labels_username}
name="email"
value={this.state.email}
onChange={this.onChange}
fullWidth={true}
/>
<TextField
// variant='outlined'
type={this.state.showPassword ? "text" : "password"}
label={Blockly.Msg.labels_password}
name="password"
value={this.state.password}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={this.handleClickShowPassword}
onMouseDown={this.handleMouseDownPassword}
edge="end"
>
<FontAwesomeIcon
size="xs"
icon={this.state.showPassword ? faEyeSlash : faEye}
/>
</IconButton>
</InputAdornment>
),
}}
onChange={this.onChange}
fullWidth={true}
/>
<p>
<Button
color="primary"
variant="contained"
type="submit"
style={{ width: "100%" }}
>
{this.props.progress ? (
<div style={{ height: "24.5px" }}>
<CircularProgress color="inherit" size={20} />
</div>
) : (
Blockly.Msg.button_login
)}
</Button>
</p>
</form>
<p style={{ textAlign: "center", fontSize: "0.8rem" }}>
<Link
rel="noreferrer"

View File

@ -1,147 +1,204 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import * as Blockly from 'blockly/core';
import * as Blockly from "blockly/core";
import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
import { withStyles } from '@material-ui/core/styles';
import Tooltip from '@material-ui/core/Tooltip';
import IconButton from '@material-ui/core/IconButton';
import Chip from '@material-ui/core/Chip';
import Avatar from '@material-ui/core/Avatar';
import Popover from '@material-ui/core/Popover';
import withWidth, { isWidthDown } from "@material-ui/core/withWidth";
import { withStyles } from "@material-ui/core/styles";
import Tooltip from "@material-ui/core/Tooltip";
import IconButton from "@material-ui/core/IconButton";
import Chip from "@material-ui/core/Chip";
import Avatar from "@material-ui/core/Avatar";
import Popover from "@material-ui/core/Popover";
import { faPuzzlePiece, faTrash, faPlus, faPen, faArrowsAlt, faEllipsisH } from "@fortawesome/free-solid-svg-icons";
import {
faPuzzlePiece,
faTrash,
faPlus,
faPen,
faArrowsAlt,
faEllipsisH,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const styles = (theme) => ({
stats: {
backgroundColor: theme.palette.primary.main,
display: 'inline',
marginLeft: '50px',
padding: '3px 10px',
display: "inline",
marginLeft: "50px",
padding: "3px 10px",
// borderRadius: '25%'
},
menu: {
backgroundColor: theme.palette.secondary.main,
color: theme.palette.secondary.contrastText,
width: '40px',
height: '40px',
'&:hover': {
width: "40px",
height: "40px",
"&:hover": {
backgroundColor: theme.palette.secondary.main,
color: theme.palette.primary.main,
}
}
},
},
});
class WorkspaceStats extends Component {
state = {
anchor: null
}
anchor: null,
};
handleClose = () => {
this.setState({ anchor: null });
}
};
handleClick = (event) => {
this.setState({ anchor: event.currentTarget });
};
render() {
const bigDisplay = !isWidthDown('sm', this.props.width);
const bigDisplay = !isWidthDown("sm", this.props.width);
const workspace = Blockly.getMainWorkspace();
const remainingBlocksInfinity = workspace ? workspace.remainingCapacity() !== Infinity : null;
const stats = <div style={bigDisplay ? { display: 'flex' } : { display: 'inline' }}>
<Tooltip title="Anzahl aktueller Blöcke" arrow>
<Chip
style={bigDisplay ? { marginRight: '1rem' } : { marginRight: '1rem', marginBottom: '5px' }}
color="primary"
avatar={<Avatar><FontAwesomeIcon icon={faPuzzlePiece} /></Avatar>}
label={workspace ? workspace.getAllBlocks().length : 0}>
</Chip>
</Tooltip>
<Tooltip title="Anzahl neuer Blöcke" arrow>
<Chip
style={bigDisplay ? { marginRight: '1rem' } : { marginRight: '1rem', marginBottom: '5px' }}
color="primary"
avatar={<Avatar><FontAwesomeIcon icon={faPlus} /></Avatar>}
label={this.props.create > 0 ? this.props.create : 0}> {/* initialXML is created automatically, Block is not part of the statistics */}
</Chip>
</Tooltip>
<Tooltip title="Anzahl veränderter Blöcke" arrow>
<Chip
style={bigDisplay ? { marginRight: '1rem' } : { marginRight: '1rem', marginBottom: '5px' }}
color="primary"
avatar={<Avatar><FontAwesomeIcon icon={faPen} /></Avatar>}
label={this.props.change}>
</Chip>
</Tooltip>
<Tooltip title="Anzahl bewegter Blöcke" arrow>
<Chip
style={bigDisplay ? { marginRight: '1rem' } : { marginRight: '1rem', marginBottom: '5px' }}
color="primary"
avatar={<Avatar><FontAwesomeIcon icon={faArrowsAlt} /></Avatar>}
label={this.props.move > 0 ? this.props.move : 0}> {/* initialXML is moved automatically, Block is not part of the statistics */}
</Chip>
</Tooltip>
<Tooltip title="Anzahl gelöschter Blöcke" arrow>
<Chip
style={remainingBlocksInfinity ? bigDisplay ? { marginRight: '1rem' } : { marginRight: '1rem', marginBottom: '5px' } : {}}
color="primary"
avatar={<Avatar><FontAwesomeIcon icon={faTrash} /></Avatar>}
label={this.props.delete}>
</Chip>
</Tooltip>
{remainingBlocksInfinity ?
<Tooltip title="Verbleibende Blöcke" arrow>
const remainingBlocksInfinity = workspace
? workspace.remainingCapacity() !== Infinity
: null;
const stats = (
<div style={bigDisplay ? { display: "flex" } : { display: "inline" }}>
<Tooltip title={Blockly.Msg.tooltip_statistics_current} arrow>
<Chip
style={bigDisplay ? { marginRight: '1rem' } : { marginRight: '1rem', marginBottom: '5px' }}
style={
bigDisplay
? { marginRight: "1rem" }
: { marginRight: "1rem", marginBottom: "5px" }
}
color="primary"
label={workspace.remainingCapacity()}>
</Chip>
</Tooltip> : null}
</div>
return (
bigDisplay ?
<div style={{ bottom: 0, position: 'absolute' }}>
{stats}
</div>
:
<div>
<Tooltip title='Statistiken anzeigen' arrow>
<IconButton
className={this.props.classes.menu}
onClick={(event) => this.handleClick(event)}
>
<FontAwesomeIcon icon={faEllipsisH} size="xs" />
</IconButton>
</Tooltip>
<Popover
open={Boolean(this.state.anchor)}
anchorEl={this.state.anchor}
onClose={this.handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
PaperProps={{
style: { margin: '5px' }
}}
avatar={
<Avatar>
<FontAwesomeIcon icon={faPuzzlePiece} />
</Avatar>
}
label={workspace ? workspace.getAllBlocks().length : 0}
></Chip>
</Tooltip>
<Tooltip title={Blockly.Msg.tooltip_statistics_new} arrow>
<Chip
style={
bigDisplay
? { marginRight: "1rem" }
: { marginRight: "1rem", marginBottom: "5px" }
}
color="primary"
avatar={
<Avatar>
<FontAwesomeIcon icon={faPlus} />
</Avatar>
}
label={this.props.create > 0 ? this.props.create : 0}
>
<div style={{ margin: '10px' }}>
{stats}
</div>
</Popover>
</div>
{" "}
{/* initialXML is created automatically, Block is not part of the statistics */}
</Chip>
</Tooltip>
<Tooltip title={Blockly.Msg.tooltip_statistics_changed} arrow>
<Chip
style={
bigDisplay
? { marginRight: "1rem" }
: { marginRight: "1rem", marginBottom: "5px" }
}
color="primary"
avatar={
<Avatar>
<FontAwesomeIcon icon={faPen} />
</Avatar>
}
label={this.props.change}
></Chip>
</Tooltip>
<Tooltip title={Blockly.Msg.tooltip_statistics_moved} arrow>
<Chip
style={
bigDisplay
? { marginRight: "1rem" }
: { marginRight: "1rem", marginBottom: "5px" }
}
color="primary"
avatar={
<Avatar>
<FontAwesomeIcon icon={faArrowsAlt} />
</Avatar>
}
label={this.props.move > 0 ? this.props.move : 0}
>
{" "}
{/* initialXML is moved automatically, Block is not part of the statistics */}
</Chip>
</Tooltip>
<Tooltip title={Blockly.Msg.tooltip_statistics_deleted} arrow>
<Chip
style={
remainingBlocksInfinity
? bigDisplay
? { marginRight: "1rem" }
: { marginRight: "1rem", marginBottom: "5px" }
: {}
}
color="primary"
avatar={
<Avatar>
<FontAwesomeIcon icon={faTrash} />
</Avatar>
}
label={this.props.delete}
></Chip>
</Tooltip>
{remainingBlocksInfinity ? (
<Tooltip title={Blockly.Msg.tooltip_statistics_remaining} arrow>
<Chip
style={
bigDisplay
? { marginRight: "1rem" }
: { marginRight: "1rem", marginBottom: "5px" }
}
color="primary"
label={workspace.remainingCapacity()}
></Chip>
</Tooltip>
) : null}
</div>
);
};
return bigDisplay ? (
<div style={{ bottom: 0, position: "absolute" }}>{stats}</div>
) : (
<div>
<Tooltip title={Blockly.Msg.tooltip_statistics_show} arrow>
<IconButton
className={this.props.classes.menu}
onClick={(event) => this.handleClick(event)}
>
<FontAwesomeIcon icon={faEllipsisH} size="xs" />
</IconButton>
</Tooltip>
<Popover
open={Boolean(this.state.anchor)}
anchorEl={this.state.anchor}
onClose={this.handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
PaperProps={{
style: { margin: "5px" },
}}
>
<div style={{ margin: "10px" }}>{stats}</div>
</Popover>
</div>
);
}
}
WorkspaceStats.propTypes = {
@ -149,15 +206,18 @@ WorkspaceStats.propTypes = {
change: PropTypes.number.isRequired,
delete: PropTypes.number.isRequired,
move: PropTypes.number.isRequired,
workspaceChange: PropTypes.number.isRequired
workspaceChange: PropTypes.number.isRequired,
};
const mapStateToProps = state => ({
const mapStateToProps = (state) => ({
create: state.workspace.stats.create,
change: state.workspace.stats.change,
delete: state.workspace.stats.delete,
move: state.workspace.stats.move,
workspaceChange: state.workspace.change
workspaceChange: state.workspace.change,
});
export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(withWidth()(WorkspaceStats)));
export default connect(
mapStateToProps,
null
)(withStyles(styles, { withTheme: true })(withWidth()(WorkspaceStats)));

View File

@ -1,21 +1,22 @@
import * as Blockly from 'blockly/core';
import * as Blockly from "blockly/core";
export const FaqQuestions = () => {
return (
[
{
question: `${Blockly.Msg.faq_q1_question}`,
answer: `${Blockly.Msg.faq_q1_answer}`,
},
{
question: `${Blockly.Msg.faq_q2_question}`,
answer: `${Blockly.Msg.faq_q2_answer}`,
},
{
question: `${Blockly.Msg.faq_q3_question}`,
answer: `${Blockly.Msg.faq_q3_answer}`,
},
])
}
return [
{
question: `${Blockly.Msg.faq_q1_question}`,
answer: `${Blockly.Msg.faq_q1_answer}`,
},
{
question: `${Blockly.Msg.faq_q2_question}`,
answer: `${Blockly.Msg.faq_q2_answer}`,
},
{
question: `${Blockly.Msg.faq_tablet_question}`,
answer: `${Blockly.Msg.faq_tablet_answer}`,
},
{
question: `${Blockly.Msg.faq_q3_question}`,
answer: `${Blockly.Msg.faq_q3_answer}`,
},
];
};

View File

@ -86,7 +86,7 @@
{
"id": "bluetooth-bee",
"name": "Bluetooth-Bee",
"src": "lora-bee.png",
"src": "ble-bee.png",
"url": "https://docs.sensebox.de/hardware/bee-ble/"
},
{

17032
yarn.lock

File diff suppressed because it is too large Load Diff