diff --git a/package-lock.json b/package-lock.json index 904c851..04700ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@fortawesome/free-solid-svg-icons": "^5.14.0", "@fortawesome/react-fontawesome": "^0.1.11", "@material-ui/core": "^4.11.0", + "@monaco-editor/react": "^4.3.1", "@sentry/react": "^6.0.0", "@sentry/tracing": "^6.0.0", "@testing-library/jest-dom": "^4.2.4", @@ -3120,6 +3121,31 @@ "node": ">=8.0.0" } }, + "node_modules/@monaco-editor/loader": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.2.0.tgz", + "integrity": "sha512-cJVCG/T/KxXgzYnjKqyAgsKDbH9mGLjcXxN6AmwumBwa2rVFkwvGcUj1RJtD0ko4XqLqJxwqsN/Z/KURB5f1OQ==", + "dependencies": { + "state-local": "^1.0.6" + }, + "peerDependencies": { + "monaco-editor": ">= 0.21.0 < 1" + } + }, + "node_modules/@monaco-editor/react": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.3.1.tgz", + "integrity": "sha512-f+0BK1PP/W5I50hHHmwf11+Ea92E5H1VZXs+wvKplWUWOfyMa1VVwqkJrXjRvbcqHL+XdIGYWhWNdi4McEvnZg==", + "dependencies": { + "@monaco-editor/loader": "^1.2.0", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -16643,6 +16669,12 @@ "node": "*" } }, + "node_modules/monaco-editor": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz", + "integrity": "sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==", + "peer": true + }, "node_modules/move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -21503,6 +21535,11 @@ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==" }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" + }, "node_modules/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -27515,6 +27552,23 @@ "react-is": "^16.8.0" } }, + "@monaco-editor/loader": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.2.0.tgz", + "integrity": "sha512-cJVCG/T/KxXgzYnjKqyAgsKDbH9mGLjcXxN6AmwumBwa2rVFkwvGcUj1RJtD0ko4XqLqJxwqsN/Z/KURB5f1OQ==", + "requires": { + "state-local": "^1.0.6" + } + }, + "@monaco-editor/react": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.3.1.tgz", + "integrity": "sha512-f+0BK1PP/W5I50hHHmwf11+Ea92E5H1VZXs+wvKplWUWOfyMa1VVwqkJrXjRvbcqHL+XdIGYWhWNdi4McEvnZg==", + "requires": { + "@monaco-editor/loader": "^1.2.0", + "prop-types": "^15.7.2" + } + }, "@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -38126,6 +38180,12 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz", "integrity": "sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA==" }, + "monaco-editor": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz", + "integrity": "sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==", + "peer": true + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -42087,6 +42147,11 @@ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==" }, + "state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", diff --git a/package.json b/package.json index 3ceca2c..8eaeb1a 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@fortawesome/free-solid-svg-icons": "^5.14.0", "@fortawesome/react-fontawesome": "^0.1.11", "@material-ui/core": "^4.11.0", + "@monaco-editor/react": "^4.3.1", "@sentry/react": "^6.0.0", "@sentry/tracing": "^6.0.0", "@testing-library/jest-dom": "^4.2.4", diff --git a/src/App.css b/src/App.css index 1a8a13d..9903a22 100644 --- a/src/App.css +++ b/src/App.css @@ -1,51 +1,50 @@ .wrapper { - min-height: calc(100vh - 60px); /* will cover the 100% of viewport - height of footer (padding-bottom) */ + min-height: calc( + 100vh - 60px + ); /* will cover the 100% of viewport - height of footer (padding-bottom) */ overflow: hidden; display: block; position: relative; padding-bottom: 60px; /* height of your footer + 30px*/ } - -.tutorial img{ +.tutorial img { display: flex; align-items: center; - max-height: 40vH; + max-height: 40vh; max-width: 100%; margin: auto; -} - -.news img{ - display: flex; - align-items: center; - max-height: 40vH; - max-width: 100%; - margin: auto; -} - -.tutorial blockquote{ - background: #f9f9f9; - border-left: 10px solid#4EAF47; - margin: 1.5em 10px; - padding: 0.5em 10px; - quotes: "\201C""\201D""\2018""\2019"; - } - blockquote:before { - color:#4EAF47; - content: open-quote; - font-size: 4em; - line-height: 0.1em; - margin-right: 0.25em; - vertical-align: -0.4em; - } - blockquote p { - display: inline; - } - -.overlay { - display: flex; - flex-direction: column; - align-items: center; } - +.news img { + display: flex; + align-items: center; + max-height: 40vh; + max-width: 100%; + margin: auto; +} + +.tutorial blockquote { + background: #f9f9f9; + border-left: 10px solid#4EAF47; + margin: 1.5em 10px; + padding: 0.5em 10px; + quotes: "\201C""\201D""\2018""\2019"; +} +blockquote:before { + color: #4eaf47; + content: open-quote; + font-size: 4em; + line-height: 0.1em; + margin-right: 0.25em; + vertical-align: -0.4em; +} +blockquote p { + display: inline; +} + +.overlay { + display: flex; + flex-direction: column; + align-items: center; +} diff --git a/src/components/CodeEditor/CodeEditor.js b/src/components/CodeEditor/CodeEditor.js new file mode 100644 index 0000000..5322ad3 --- /dev/null +++ b/src/components/CodeEditor/CodeEditor.js @@ -0,0 +1,294 @@ +import React from "react"; +import { useState, useRef } from "react"; +import { default as MonacoEditor } from "@monaco-editor/react"; +import { withRouter } from "react-router-dom"; +import { Button, Grid } from "@material-ui/core"; +import Blockly from "blockly/core"; +import Divider from "@material-ui/core/Divider"; +import { saveAs } from "file-saver"; +import Drawer from "@material-ui/core/Drawer"; +import Sidebar from "./Sidebar"; +import Dialog from "../Dialog"; +import SaveIcon from "./SaveIcon"; +import store from "../../store"; + +const CodeEditor = (props) => { + const [fileHandle, setFileHandle] = useState(); + const [fileContent, setFileContent] = useState(""); + const [progress, setProgress] = useState(false); + const [id, setId] = useState(""); + const [open, setOpen] = useState(false); + const [error, setError] = useState(""); + const editorRef = useRef(null); + const [autoSave, setAutoSave] = useState(false); + const [time, setTime] = useState(null); + const [value, setValue] = useState(""); + const [resetDialog, setResetDialog] = useState(false); + const [blocklyCode, setBlocklyCode] = useState(""); + const [defaultValue, setDefaultValue] = useState( + localStorage.getItem("ArduinoCode") + ? localStorage.getItem("ArduinoCode") + : ` +#include //needs to be always included + +void setup () { + +} + +void loop() { + +}` + ); + + const compile = () => { + setProgress(true); + const data = { + board: process.env.REACT_APP_BOARD, + sketch: editorRef.current.getValue(), + }; + fetch(`${process.env.REACT_APP_COMPILER_URL}/compile`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data), + }) + .then((response) => response.json()) + .then((data) => { + console.log(data); + if (data.code === "Internal Server Error") { + setProgress(false); + setOpen(true); + setError(data.message); + } + setProgress(false); + const result = data.data.id; + setId(result); + console.log(result); + const filename = "sketch"; + window.open( + `${process.env.REACT_APP_COMPILER_URL}/download?id=${result}&board=${process.env.REACT_APP_BOARD}&filename=${filename}`, + "_self" + ); + }) + .catch((err) => { + console.log(err); + }); + }; + + const saveIno = () => { + var filename = "sketch"; + var code = editorRef.current.getValue(); + + filename = `${filename}.ino`; + var blob = new Blob([code], { type: "text/plain;charset=utf-8" }); + saveAs(blob, filename); + }; + + const openIno = async () => { + const [myFileHandle] = await window.showOpenFilePicker(); + setFileHandle(myFileHandle); + + const file = await myFileHandle.getFile(); + const contents = await file.text(); + setFileContent(contents); + }; + + const toggleDrawer = (anchor, open) => (event) => { + if ( + event.type === "keydown" && + (event.key === "Tab" || event.key === "Shift") + ) { + return; + } + + setOpen(false); + }; + + const resetCode = () => { + const resetCode = ` +#include //needs to be always included + +void setup () { + +} + +void loop() { + +}`; + + editorRef.current.setValue(resetCode); + }; + + const resetTimeout = (id, newID) => { + clearTimeout(id); + return newID; + }; + + const editValue = (value) => { + setTime(resetTimeout(time, setTimeout(saveValue, 400))); + setValue(value); + }; + + const saveValue = () => { + localStorage.setItem("ArduinoCode", value); + setAutoSave(true); + setTimeout(() => setAutoSave(false), 1000); + }; + + const handleClose = (event, reason) => { + if (reason === "clickaway") { + return; + } + + setOpen(false); + }; + + const getBlocklyCode = () => { + var code = store.getState().workspace.code.arduino; + editorRef.current.setValue(code); + }; + + return ( +
+ + +

+ {Blockly.Msg.drawer_ideerror_head} +

+

+ {Blockly.Msg.drawer_ideerror_text} +

+ +

+ {" "} + {`${error}`}{" "} +

+
+ +
+

Code Editor

+ +
+ + { + editValue(value); + }} + defaultLanguage="cpp" + defaultValue={defaultValue} + value={fileContent} + onMount={(editor, monaco) => { + editorRef.current = editor; + }} + /> +
+ + + + + + + + +
+ Dein Code wird nun kompiliert und anschließend auf deinen Computer + heruntergeladen +
+
{" "} + { + setResetDialog(false); + }} + onClick={() => { + setResetDialog(false); + }} + button={Blockly.Msg.button_cancel} + > + {" "} +
+ +
+
+
+
+
+ ); +}; + +export default withRouter(CodeEditor); diff --git a/src/components/CodeEditor/Compile.js b/src/components/CodeEditor/Compile.js new file mode 100644 index 0000000..3c3a6a7 --- /dev/null +++ b/src/components/CodeEditor/Compile.js @@ -0,0 +1,332 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { workspaceName } from "../../actions/workspaceActions"; + +import { detectWhitespacesAndReturnReadableResult } from "../../helpers/whitespace"; + +import { withStyles } from "@material-ui/core/styles"; +import Button from "@material-ui/core/Button"; +import Backdrop from "@material-ui/core/Backdrop"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import IconButton from "@material-ui/core/IconButton"; +import Tooltip from "@material-ui/core/Tooltip"; +import Divider from "@material-ui/core/Divider"; +import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import * as Blockly from "blockly/core"; +import Copy from "../copy.svg"; + +import MuiDrawer from "@material-ui/core/Drawer"; +import Dialog from "../Dialog"; + +const styles = (theme) => ({ + backdrop: { + zIndex: theme.zIndex.drawer + 1, + color: "#fff", + }, + iconButton: { + backgroundColor: theme.palette.button.compile, + color: theme.palette.primary.contrastText, + width: "40px", + height: "40px", + "&:hover": { + backgroundColor: theme.palette.button.compile, + color: theme.palette.primary.contrastText, + }, + }, + button: { + backgroundColor: theme.palette.button.compile, + color: theme.palette.primary.contrastText, + "&:hover": { + backgroundColor: theme.palette.button.compile, + color: theme.palette.primary.contrastText, + }, + }, +}); + +const Drawer = withStyles((theme) => ({ + paperAnchorBottom: { + backgroundColor: "black", + height: "20vH", + }, +}))(MuiDrawer); + +class Compile extends Component { + constructor(props) { + super(props); + this.state = { + progress: false, + open: false, + file: false, + title: "", + content: "", + name: props.name, + error: "", + appLink: "", + appDialog: false, + }; + } + + componentDidMount() {} + + componentDidUpdate(props) { + if (props.name !== this.props.name) { + this.setState({ name: this.props.name }); + } + } + + compile = () => { + this.setState({ progress: true }); + const data = { + board: process.env.REACT_APP_BOARD, + sketch: this.props.arduino, + }; + fetch(`${process.env.REACT_APP_COMPILER_URL}/compile`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data), + }) + .then((response) => response.json()) + .then((data) => { + console.log(data); + if (data.code === "Internal Server Error") { + this.setState({ + progress: false, + file: false, + open: true, + title: Blockly.Msg.compiledialog_headline, + content: Blockly.Msg.compiledialog_text, + error: data.message, + }); + } + this.setState({ id: data.data.id }, () => { + this.createFileName(); + }); + }) + .catch((err) => { + console.log(err); + //this.setState({ progress: false, file: false, open: true, title: Blockly.Msg.compiledialog_headline, content: Blockly.Msg.compiledialog_text }); + }); + }; + + download = () => { + const id = this.state.id; + const filename = detectWhitespacesAndReturnReadableResult(this.state.name); + this.toggleDialog(); + this.props.workspaceName(this.state.name); + window.open( + `${process.env.REACT_APP_COMPILER_URL}/download?id=${id}&board=${process.env.REACT_APP_BOARD}&filename=${filename}`, + "_self" + ); + this.setState({ progress: false }); + }; + + toggleDialog = () => { + this.setState({ open: !this.state, progress: false, appDialog: false }); + }; + + createFileName = () => { + if (this.props.platform === true) { + const filename = detectWhitespacesAndReturnReadableResult( + this.state.name + ); + this.setState({ + link: `blocklyconnect-app://sketch/${filename}/${this.state.id}`, + }); + this.setState({ appDialog: true }); + } else { + if (this.state.name) { + this.download(); + } else { + this.setState({ + file: true, + open: true, + title: "Projekt kompilieren", + content: + "Bitte gib einen Namen für die Bennenung des zu kompilierenden Programms ein und bestätige diesen mit einem Klick auf 'Eingabe'.", + }); + } + } + + // if (this.state.name) { + // this.download(); + // } else { + // this.setState({ + // file: true, + // open: true, + // title: "Projekt kompilieren", + // content: + // "Bitte gib einen Namen für die Bennenung des zu kompilierenden Programms ein und bestätige diesen mit einem Klick auf 'Eingabe'.", + // }); + // } + }; + + setFileName = (e) => { + this.setState({ name: e.target.value }); + }; + + toggleDrawer = (anchor, open) => (event) => { + if ( + event.type === "keydown" && + (event.key === "Tab" || event.key === "Shift") + ) { + return; + } + + this.setState({ open: false }); + }; + + render() { + return ( +
+ {this.props.iconButton ? ( + + this.compile()} + > + + + + ) : ( + + )} + + {this.props.platform === false ? ( + +
+ copyimage +

{Blockly.Msg.compile_overlay_head}

+

{Blockly.Msg.compile_overlay_text}

+

+ {Blockly.Msg.compile_overlay_help} + + FAQ + +

+ +
+
+ ) : ( + +
+ {/* copyimage */} +

Dein Code wird kompiliert!

+

übertrage ihn anschließend mithlfe der senseBoxConnect-App

+

+ {Blockly.Msg.compile_overlay_help} + + FAQ + +

+ +
+
+ )} + +

+ {Blockly.Msg.drawer_ideerror_head} +

+

+ {Blockly.Msg.drawer_ideerror_text} +

+ +

+ {" "} + {`${this.state.error}`}{" "} +

+
+ +
+

Dein Code wurde erfolgreich kompiliert

+ + + +
+
+
+ ); + } +} + +Compile.propTypes = { + arduino: PropTypes.string.isRequired, + name: PropTypes.string, + workspaceName: PropTypes.func.isRequired, + platform: PropTypes.bool.isRequired, +}; + +const mapStateToProps = (state) => ({ + arduino: state.workspace.code.arduino, + name: state.workspace.name, + platform: state.general.platform, +}); + +export default connect(mapStateToProps, { workspaceName })( + withStyles(styles, { withTheme: true })(Compile) +); diff --git a/src/components/CodeEditor/SaveIcon.js b/src/components/CodeEditor/SaveIcon.js new file mode 100644 index 0000000..c776485 --- /dev/null +++ b/src/components/CodeEditor/SaveIcon.js @@ -0,0 +1,40 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faCircleNotch, faSave } from "@fortawesome/free-solid-svg-icons"; +import Tooltip from "@material-ui/core/Tooltip"; +import React from "react"; + +const SaveIcon = ({ loading }) => ( + +
+ {loading && ( + + )} + +
+
+); + +export default SaveIcon; diff --git a/src/components/CodeEditor/SerialMonitor.js b/src/components/CodeEditor/SerialMonitor.js new file mode 100644 index 0000000..b40f10b --- /dev/null +++ b/src/components/CodeEditor/SerialMonitor.js @@ -0,0 +1,92 @@ +import { useState } from "react"; +import Button from "@material-ui/core/Button"; + +const SerialMonitor = () => { + const [serialPortContent, setSerialPortContent] = useState([]); + + const [checked, setChecked] = useState(false); + const handleClick = () => setChecked(!checked); + + const connectPort = async () => { + try { + const port = await navigator.serial.requestPort(); + + await port.open({ baudRate: 9600 }); + + while (port.readable) { + const reader = port.readable.getReader(); + + try { + while (true) { + const { value, done } = await reader.read(); + if (done) { + // Allow the serial port to be closed later. + reader.releaseLock(); + break; + } + if (value) { + // byte array to string: https://stackoverflow.com/a/37542820 + const text = String.fromCharCode.apply(null, value); + console.log(text); + setSerialPortContent((prevContent) => [ + ...prevContent, + [new Date(), text], + ]); + } + } + } catch (error) { + setSerialPortContent((prevContent) => [ + ...prevContent, + [new Date(), error], + ]); + } + } + } catch (error) { + setSerialPortContent((prevContent) => [ + ...prevContent, + [new Date(), error], + ]); + } + }; + + return ( + <> +
+ + + +
+
+ {serialPortContent.map((log) => { + return ( +

+ {checked && ( + {log[0].toISOString()} + )} + + {log[1]} +

+ ); + })} +
+ + ); +}; + +export default SerialMonitor; diff --git a/src/components/CodeEditor/Sidebar.js b/src/components/CodeEditor/Sidebar.js new file mode 100644 index 0000000..a07435d --- /dev/null +++ b/src/components/CodeEditor/Sidebar.js @@ -0,0 +1,145 @@ +import React, { useEffect } from "react"; +import Blockly from "blockly"; +import Accordion from "@material-ui/core/Accordion"; +import AccordionSummary from "@material-ui/core/AccordionSummary"; +import AccordionDetails from "@material-ui/core/AccordionDetails"; +import Typography from "@material-ui/core/Typography"; +import { LibraryVersions } from "../../data/versions.js"; +import { useMonaco } from "@monaco-editor/react"; +import { Button } from "@material-ui/core"; +import Dialog from "../Dialog"; +import SerialMonitor from "./SerialMonitor.js"; +import axios from "axios"; + +const Sidebar = () => { + const [alert, setAlert] = React.useState(false); + const [examples, setExamples] = React.useState([]); + + useEffect(() => { + axios + .get("https://coelho.opensensemap.org/items/blocklysamples") + .then((res) => { + setExamples(res.data.data); + }); + }, []); + + const monaco = useMonaco(); + const loadCode = (code) => { + console.log(code); + console.log(monaco); + const defaultCode = ` +void setup () { + +} + +void loop(){ + +}`; + var currentCode = monaco.editor.getModels()[0].getValue(); + + setAlert(true); + monaco.editor.getModels()[0].setValue(code); + }; + + const toggleDialog = () => { + setAlert(false); + }; + + return ( +
+ {"serial" in navigator ? ( + + + Serial Monitor + + + + + + + + ) : null} + + + + Beispiele + + + + {examples.map((object, i) => { + return ( + + ); + })} + + + + + + Installierte Libraries + + + +

+ For Dokumentation take a look at the installed libraries and their + source +

+ {LibraryVersions().map((object, i) => { + return ( +

+ + {object.library} {object.version} + +

+ ); + })} +
+
+ toggleDialog()} + onClick={() => toggleDialog()} + button={Blockly.Msg.button_close} + > +
{Blockly.Msg.tabletDialog_text}
+
+ {Blockly.Msg.tabletDialog_more}{" "} + + https://sensebox.de/app + +
+
+
+
+ ); +}; + +export default Sidebar; diff --git a/src/components/CodeViewer.js b/src/components/CodeViewer.js index 856272c..6141130 100644 --- a/src/components/CodeViewer.js +++ b/src/components/CodeViewer.js @@ -1,30 +1,25 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; - -import Prism from "prismjs"; -import "prismjs/themes/prism.css"; -import "prismjs/plugins/line-numbers/prism-line-numbers"; -import "prismjs/plugins/line-numbers/prism-line-numbers.css"; - -import withWidth from '@material-ui/core/withWidth'; -import { withStyles } from '@material-ui/core/styles'; -import MuiAccordion from '@material-ui/core/Accordion'; -import MuiAccordionSummary from '@material-ui/core/AccordionSummary'; -import MuiAccordionDetails from '@material-ui/core/AccordionDetails'; -import { Card } from '@material-ui/core'; -import * as Blockly from 'blockly' +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import withWidth from "@material-ui/core/withWidth"; +import { withStyles } from "@material-ui/core/styles"; +import MuiAccordion from "@material-ui/core/Accordion"; +import MuiAccordionSummary from "@material-ui/core/AccordionSummary"; +import MuiAccordionDetails from "@material-ui/core/AccordionDetails"; +import { Card } from "@material-ui/core"; +import * as Blockly from "blockly"; +import { default as MonacoEditor } from "@monaco-editor/react"; const Accordion = withStyles((theme) => ({ root: { border: `1px solid ${theme.palette.secondary.main}`, - boxShadow: 'none', - '&:before': { - display: 'none', + boxShadow: "none", + "&:before": { + display: "none", }, - '&$expanded': { - margin: 'auto', + "&$expanded": { + margin: "auto", }, }, expanded: {}, @@ -34,15 +29,15 @@ const AccordionSummary = withStyles((theme) => ({ root: { backgroundColor: theme.palette.secondary.main, borderBottom: `1px solid white`, - marginBottom: '-1px', - minHeight: '50px', - '&$expanded': { - minHeight: '50px', + marginBottom: "-1px", + minHeight: "50px", + "&$expanded": { + minHeight: "50px", }, }, content: { - '&$expanded': { - margin: '12px 0', + "&$expanded": { + margin: "12px 0", }, }, expanded: {}, @@ -54,40 +49,60 @@ const AccordionDetails = withStyles((theme) => ({ }, }))(MuiAccordionDetails); - class CodeViewer extends Component { - constructor(props) { super(props); this.state = { + code: this.props.arduino, + changed: false, expanded: true, - componentHeight: null + componentHeight: null, }; this.myDiv = React.createRef(); } componentDidMount() { - Prism.highlightAll(); - this.setState({ componentHeight: this.myDiv.current.offsetHeight + 'px' }); + this.setState({ componentHeight: this.myDiv.current.offsetHeight + "px" }); } - componentDidUpdate(props, state) { - if (this.myDiv.current && this.myDiv.current.offsetHeight + 'px' !== this.state.componentHeight) { - this.setState({ componentHeight: this.myDiv.current.offsetHeight + 'px' }); + componentDidUpdate(prevProps, prevState) { + // if (this.props.arduino !== prevProps.arduino) { + // this.setState({ changed: true }); + + // console.log(`code changed: ${this.state.changed}`); + // if (this.state.changed && prevState.code !== this.props.arduino) { + // this.setState({ code: this.props.arduino }); + // this.setState({ changed: false }); + // } + + // if (this.state.code !== prevState.code && this.state.changed) { + // this.setState({ changed: false }); + // } + + // if (this.props.arduino !== this.state.code) { + // this.setState({ changed: true }); + // //this.setState({ code: this.props.arduino }); + // } + + if ( + this.myDiv.current && + this.myDiv.current.offsetHeight + "px" !== this.state.componentHeight + ) { + this.setState({ + componentHeight: this.myDiv.current.offsetHeight + "px", + }); } - Prism.highlightAll(); } onChange = () => { this.setState({ expanded: !this.state.expanded }); - - } + }; render() { - var curlyBrackets = '{ }'; - var unequal = '<>'; + var curlyBrackets = "{ }"; + var unequal = "<>"; return ( - + - {curlyBrackets} -
{Blockly.Msg.codeviewer_arduino}
+ + {curlyBrackets} + +
+ {Blockly.Msg.codeviewer_arduino} +
- -
-              
-                {this.props.arduino}
-              
-            
+ +
- {unequal} -
{Blockly.Msg.codeviewer_xml}
+ + {unequal} + +
+ {Blockly.Msg.codeviewer_xml} +
- -
-              
-                {`${this.props.xml}`}
-              
-            
+ +
); - }; + } } CodeViewer.propTypes = { arduino: PropTypes.string.isRequired, xml: PropTypes.string.isRequired, - tooltip: PropTypes.string.isRequired + tooltip: PropTypes.string.isRequired, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ arduino: state.workspace.code.arduino, xml: state.workspace.code.xml, - tooltip: state.workspace.code.tooltip + tooltip: state.workspace.code.tooltip, }); export default connect(mapStateToProps, null)(withWidth()(CodeViewer)); diff --git a/src/components/Home.js b/src/components/Home.js index 171a61f..de58de8 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -22,7 +22,7 @@ import { faCode } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import TooltipViewer from "./TooltipViewer"; import Dialog from "./Dialog"; - +// import Autosave from "./Workspace/AutoSave"; const styles = (theme) => ({ codeOn: { backgroundColor: theme.palette.primary.main, @@ -54,6 +54,7 @@ class Home extends Component { key: "", message: "", open: true, + initialXml: localStorage.getItem("autoSaveXML"), }; } @@ -119,10 +120,12 @@ class Home extends Component { ) : null} +
+ {/* */} +
{this.props.project ? ( @@ -169,7 +173,10 @@ class Home extends Component { initialXml={this.props.project.xml} /> ) : ( - + )}
diff --git a/src/components/Navbar.js b/src/components/Navbar.js index 8977637..9eaf29a 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -21,6 +21,7 @@ import ListItemIcon from "@material-ui/core/ListItemIcon"; import ListItemText from "@material-ui/core/ListItemText"; import LinearProgress from "@material-ui/core/LinearProgress"; import Tour from "reactour"; +import { Badge } from "@material-ui/core"; import { home, assessment } from "./Tour"; import { faBars, @@ -34,6 +35,7 @@ import { faChalkboardTeacher, faTools, faLightbulb, + faCode, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as Blockly from "blockly"; @@ -230,6 +232,11 @@ class Navbar extends Component { icon: faLightbulb, link: "/gallery", }, + { + text: "CodeEditor", + icon: faCode, + link: "/codeeditor", + }, { text: Blockly.Msg.navbar_projects, icon: faLayerGroup, @@ -253,7 +260,13 @@ class Navbar extends Component { - + {item.text === "CodeEditor" ? ( + + + + ) : ( + + )} ); diff --git a/src/components/Route/Routes.js b/src/components/Route/Routes.js index 8bd9239..844fb66 100644 --- a/src/components/Route/Routes.js +++ b/src/components/Route/Routes.js @@ -24,6 +24,7 @@ import Login from "../User/Login"; import Account from "../User/Account"; import News from "../News"; import Faq from "../Faq"; +import CodeEditor from "../CodeEditor/CodeEditor"; class Routes extends Component { componentDidUpdate() { @@ -47,6 +48,9 @@ class Routes extends Component { + + + {/* Sharing */} diff --git a/src/components/Workspace/AutoSave.js b/src/components/Workspace/AutoSave.js new file mode 100644 index 0000000..68911a8 --- /dev/null +++ b/src/components/Workspace/AutoSave.js @@ -0,0 +1,72 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { workspaceName } from "../../actions/workspaceActions"; +import SaveIcon from "../CodeEditor/SaveIcon"; + +const resetTimeout = (id, newID) => { + clearTimeout(id); + return newID; +}; + +class AutoSave extends Component { + constructor(props) { + super(props); + this.state = { + timeout: null, + value: "", + saved: false, + autosave: false, + }; + } + + editValue = (value) => { + this.setState({ + timeout: resetTimeout( + this.state.timeout, + setTimeout(this.saveValue, 400) + ), + value: value, + }); + }; + + saveValue = () => { + this.setState({ ...this.state, saved: true }); + localStorage.setItem("autoSaveXML", this.props.xml); + setTimeout(() => this.setState({ ...this.state, saved: false }), 1000); + }; + + componentDidMount() { + console.log(this.props.xml); + } + + componentDidUpdate(prevProps) { + if (prevProps.xml !== this.props.xml) { + this.editValue(this.props.xml); + } + } + + render() { + return ( +
+ +
+ ); + } +} + +AutoSave.propTypes = { + xml: PropTypes.string.isRequired, + name: PropTypes.string, + workspaceName: PropTypes.func.isRequired, + setAutosave: PropTypes.func.isRequired, + autosave: PropTypes.bool.isRequired, +}; + +const mapStateToProps = (state) => ({ + auto: state.general.autosave, + xml: state.workspace.code.xml, + name: state.workspace.name, +}); + +export default connect(mapStateToProps, { workspaceName })(AutoSave); diff --git a/src/components/Workspace/Compile.js b/src/components/Workspace/Compile.js index 1bea26c..3c3a6a7 100644 --- a/src/components/Workspace/Compile.js +++ b/src/components/Workspace/Compile.js @@ -16,10 +16,7 @@ import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as Blockly from "blockly/core"; import Copy from "../copy.svg"; -import Prism from "prismjs"; -import "prismjs/themes/prism.css"; -import "prismjs/plugins/line-numbers/prism-line-numbers"; -import "prismjs/plugins/line-numbers/prism-line-numbers.css"; + import MuiDrawer from "@material-ui/core/Drawer"; import Dialog from "../Dialog"; @@ -71,15 +68,12 @@ class Compile extends Component { }; } - componentDidMount() { - Prism.highlightAll(); - } + componentDidMount() {} componentDidUpdate(props) { if (props.name !== this.props.name) { this.setState({ name: this.props.name }); } - Prism.highlightAll(); } compile = () => { diff --git a/src/components/Workspace/WorkspaceFunc.js b/src/components/Workspace/WorkspaceFunc.js index 68e3741..188847e 100644 --- a/src/components/Workspace/WorkspaceFunc.js +++ b/src/components/Workspace/WorkspaceFunc.js @@ -1,101 +1,113 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; - -import WorkspaceName from './WorkspaceName'; -import SaveProject from './SaveProject'; -import Compile from './Compile'; -import SolutionCheck from '../Tutorial/SolutionCheck'; -import DownloadProject from './DownloadProject'; -import OpenProject from './OpenProject'; -import Screenshot from './Screenshot'; -import ShareProject from './ShareProject'; -import ResetWorkspace from './ResetWorkspace'; -import DeleteProject from './DeleteProject'; -import CopyCode from './CopyCode'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import WorkspaceName from "./WorkspaceName"; +import SaveProject from "./SaveProject"; +import Compile from "./Compile"; +import SolutionCheck from "../Tutorial/SolutionCheck"; +import DownloadProject from "./DownloadProject"; +import OpenProject from "./OpenProject"; +import Screenshot from "./Screenshot"; +import ShareProject from "./ShareProject"; +import ResetWorkspace from "./ResetWorkspace"; +import DeleteProject from "./DeleteProject"; +import CopyCode from "./CopyCode"; +import AutoSave from "./AutoSave"; class WorkspaceFunc extends Component { + componentDidUpdate() { + console.log(this.props.autosave); + } render() { return ( -
- - {!this.props.assessment ? +
+ {!this.props.assessment & !this.props.multiple ? : null} + {!this.props.assessment ? ( - : null} + ) : null} - {this.props.assessment ? + {this.props.assessment ? ( - : !this.props.multiple ? - - : null} + ) : !this.props.multiple ? ( + + ) : null} - {!this.props.multiple ? - - : null} + {!this.props.multiple ? : null} - - {this.props.user && !this.props.multiple ? + {this.props.user && !this.props.multiple ? ( - : null} + ) : null} - {!this.props.multiple ? - - : null} + {!this.props.multiple ? ( + + ) : null} - - {!this.props.assessment && !this.props.multiple ? + {!this.props.assessment && !this.props.multiple ? ( - : null} + ) : null} - {!this.props.assessment && !this.props.multiple ? - - : null} + {!this.props.assessment && !this.props.multiple ? ( + + ) : null} - {this.props.projectType !== 'gallery' && !this.props.assessment ? + {this.props.projectType !== "gallery" && !this.props.assessment ? ( - : null} + ) : null} - {!this.props.multiple ? - - : null} + ) : null} - {!this.props.assessment && (this.props.projectType === 'project' || this.props.projectType === 'gallery') && this.props.user && this.props.user.email === this.props.project.creator ? + {!this.props.assessment && + (this.props.projectType === "project" || + this.props.projectType === "gallery") && + this.props.user && + this.props.user.email === this.props.project.creator ? ( - : null} - + ) : null}
); - }; + } } WorkspaceFunc.propTypes = { - user: PropTypes.object + user: PropTypes.object, + autosave: PropTypes.bool.isRequired, }; -const mapStateToProps = state => ({ - user: state.auth.user +const mapStateToProps = (state) => ({ + user: state.auth.user, + autosave: state.workspace.autosave, }); export default connect(mapStateToProps, null)(WorkspaceFunc); diff --git a/src/components/Workspace/WorkspaceName.js b/src/components/Workspace/WorkspaceName.js index 0437f9a..98e4c0e 100644 --- a/src/components/Workspace/WorkspaceName.js +++ b/src/components/Workspace/WorkspaceName.js @@ -1,51 +1,50 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { workspaceName } from '../../actions/workspaceActions'; -import { setDescription, updateProject } from '../../actions/projectActions'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { workspaceName } from "../../actions/workspaceActions"; +import { setDescription, updateProject } from "../../actions/projectActions"; -import Snackbar from '../Snackbar'; -import Dialog from '../Dialog'; +import Snackbar from "../Snackbar"; +import Dialog from "../Dialog"; -import withWidth, { isWidthDown } from '@material-ui/core/withWidth'; -import { withStyles } from '@material-ui/core/styles'; -import Button from '@material-ui/core/Button'; -import Tooltip from '@material-ui/core/Tooltip'; -import TextField from '@material-ui/core/TextField'; -import Typography from '@material-ui/core/Typography'; +import withWidth, { isWidthDown } from "@material-ui/core/withWidth"; +import { withStyles } from "@material-ui/core/styles"; +import Button from "@material-ui/core/Button"; +import Tooltip from "@material-ui/core/Tooltip"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; import { faPen } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import * as Blockly from 'blockly/core' +import * as Blockly from "blockly/core"; const styles = (theme) => ({ workspaceName: { + minHeight: "40px", backgroundColor: theme.palette.secondary.main, - borderRadius: '25px', - display: 'inline-flex', - cursor: 'pointer', - '&:hover': { + borderRadius: "25px", + display: "inline-flex", + cursor: "pointer", + "&:hover": { color: theme.palette.primary.main, - } - } + }, + }, }); - class WorkspaceName extends Component { - constructor(props) { super(props); this.inputRef = React.createRef(); this.state = { - title: '', - content: '', + title: "", + content: "", open: false, name: props.name, description: props.description, snackbar: false, - type: '', - key: '', - message: '' + type: "", + key: "", + message: "", }; } @@ -59,47 +58,100 @@ class WorkspaceName extends Component { } toggleDialog = () => { - this.setState({ open: !this.state, title: '', content: '' }); - } + this.setState({ open: !this.state, title: "", content: "" }); + }; setFileName = (e) => { this.setState({ name: e.target.value }); - } + }; setDescription = (e) => { this.setState({ description: e.target.value }); - } + }; renameWorkspace = () => { this.props.workspaceName(this.state.name); this.toggleDialog(); - if (this.props.projectType === 'project' || this.props.projectType === 'gallery' || this.state.projectType === 'gallery') { - if (this.props.projectType === 'gallery' || this.state.projectType === 'gallery') { + if ( + this.props.projectType === "project" || + this.props.projectType === "gallery" || + this.state.projectType === "gallery" + ) { + if ( + this.props.projectType === "gallery" || + this.state.projectType === "gallery" + ) { this.props.setDescription(this.state.description); } - if (this.state.projectType === 'gallery') { + if (this.state.projectType === "gallery") { this.saveGallery(); } else { - this.props.updateProject(this.props.projectType, this.props.project._id); + this.props.updateProject( + this.props.projectType, + this.props.project._id + ); } } else { - this.setState({ snackbar: true, type: 'success', key: Date.now(), message: `${Blockly.Msg.messages_rename_success_01} ${this.state.name} ${Blockly.Msg.messages_rename_success_02}` }); + this.setState({ + snackbar: true, + type: "success", + key: Date.now(), + message: `${Blockly.Msg.messages_rename_success_01} ${this.state.name} ${Blockly.Msg.messages_rename_success_02}`, + }); } - } + }; render() { return (
- +
{ if (this.props.multiple) { this.props.workspaceName(this.props.project.title); if (this.props.projectType === 'gallery') { this.props.setDescription(this.props.project.description); } } this.setState({ open: true, title: this.props.projectType === 'gallery' ? 'Projektdaten ändern' : this.props.projectType === 'project' ? 'Projekt umbenennen' : 'Projekt benennen', content: this.props.projectType === 'gallery' ? 'Bitte gib einen Titel und eine Beschreibung für das Galerie-Projekt ein und bestätige die Angaben mit einem Klick auf \'Eingabe\'.' : 'Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf \'Eingabe\'.' }) }} + onClick={() => { + if (this.props.multiple) { + this.props.workspaceName(this.props.project.title); + if (this.props.projectType === "gallery") { + this.props.setDescription(this.props.project.description); + } + } + this.setState({ + open: true, + title: + this.props.projectType === "gallery" + ? "Projektdaten ändern" + : this.props.projectType === "project" + ? "Projekt umbenennen" + : "Projekt benennen", + content: + this.props.projectType === "gallery" + ? "Bitte gib einen Titel und eine Beschreibung für das Galerie-Projekt ein und bestätige die Angaben mit einem Klick auf 'Eingabe'." + : "Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf 'Eingabe'.", + }); + }} > - {this.props.name && !isWidthDown(this.props.projectType === 'project' || this.props.projectType === 'gallery' ? 'xl' : 'xs', this.props.width) ? - {this.props.name} - : null} -
- + {this.props.name && + !isWidthDown( + this.props.projectType === "project" || + this.props.projectType === "gallery" + ? "xl" + : "xs", + this.props.width + ) ? ( + + {this.props.name} + + ) : null} +
+
@@ -114,23 +166,69 @@ class WorkspaceName extends Component { open={this.state.open} title={this.state.title} content={this.state.content} - onClose={() => { this.toggleDialog(); this.setState({ name: this.props.name, description: this.props.description }); }} - onClick={() => { this.toggleDialog(); this.setState({ name: this.props.name, description: this.props.description }); }} - button={'Abbrechen'} + onClose={() => { + this.toggleDialog(); + this.setState({ + name: this.props.name, + description: this.props.description, + }); + }} + onClick={() => { + this.toggleDialog(); + this.setState({ + name: this.props.name, + description: this.props.description, + }); + }} + button={"Abbrechen"} > -
- {this.props.projectType === 'gallery' || this.state.projectType === 'gallery' ? +
+ {this.props.projectType === "gallery" || + this.state.projectType === "gallery" ? (
- - + +
- : } - + ) : ( + + )} +
); - }; + } } WorkspaceName.propTypes = { @@ -142,10 +240,14 @@ WorkspaceName.propTypes = { message: PropTypes.object.isRequired, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ name: state.workspace.name, description: state.project.description, message: state.message, }); -export default connect(mapStateToProps, { workspaceName, setDescription, updateProject })(withStyles(styles, { withTheme: true })(withWidth()(WorkspaceName))); +export default connect(mapStateToProps, { + workspaceName, + setDescription, + updateProject, +})(withStyles(styles, { withTheme: true })(withWidth()(WorkspaceName))); diff --git a/src/data/arduinoExamples.js b/src/data/arduinoExamples.js new file mode 100644 index 0000000..7d2dbe4 --- /dev/null +++ b/src/data/arduinoExamples.js @@ -0,0 +1,14 @@ +export const ArduinoExamples = () => { + return [ + { + name: "MCU Component Test", + description: "Test the different compoenents of the MCU", + code: '// senseBox:home WiFi is enabled by default!\n// If you have a senseBox:home Ethernet comment out line 5\n// and comment in line 4\n// Do not comment in both at the same time!\n//#define ENABLE_ETHERNET\n#define ENABLE_WIFI\n\n#include \n#include \n#ifdef ENABLE_WIFI\n#include \n#include \n#endif\n#ifdef ENABLE_ETHERNET\n#include \n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n/********WiFi User Settings********/\nconst char *ssid = ""; // your network SSID (name)\nconst char *pass = ""; // your network password\n/**********************************/\nchar server[] = "internet-test.testing.opensensemap.org";\n#ifdef ENABLE_WIFI\nint status = WL_IDLE_STATUS;\nWiFiClient client;\n#endif\n\n#ifdef ENABLE_ETHERNET\n/********Ethernet User Settings********/\n//Configure static IP setup (only needed if DHCP is disabled)\nbyte mac[] = {0xDE,0xAD,0xBE,0xEF,0xFE,0xED};\nIPAddress myIp(192, 168, 0, 42);\nIPAddress myDns(8, 8, 8, 8);\nIPAddress myGateway(192, 168, 0, 177);\nIPAddress mySubnet(255, 255, 255, 0);\n/**********************************/\nEthernetClient ethernetClient;\n#endif\n\n#define CMD_RESET (0x00)\n#define CMD_SEND (0x03)\n#define ECC_READ (0x02) // read command\n#define ECC_WRITE (0x12) // write command\n#define ECC_ZONE_CFG (0x00) // configuration zone\n#define ECC_ZONE_CNT_FLAG (0x80) // 1=32 bytes, 0=4 bytes\nvoid setup() {\n Serial.begin(9600);\n while(!Serial); // wait until serial is ready\n printMenu();\n // power on required ports\n senseBoxIO.powerI2C(false);\n senseBoxIO.powerXB1(false);\n senseBoxIO.powerUART(false);\n delay(250);\n senseBoxIO.powerI2C(true);\n senseBoxIO.powerXB1(true);\n senseBoxIO.powerUART(true);\n // init UART and I2C\n Serial1.begin(9600);\n Serial2.begin(9600);\n Wire.begin();\n}\nvoid loop() {\n char rx;\n if (Serial.available() > 0)\n {\n\n rx = Serial.read(); // get the character\n Serial.println("\\n\\n");\n // check if a number was received\n switch(rx)\n {\n case \'1\':\n check_uart_sensor();\n Serial.println("\\nI2C/Wire:");\n byte devices, address;\n devices = 0;\n for(address = 1; address < 127; address++ )\n {\n Wire.beginTransmission(address);\n byte error = Wire.endTransmission();\n\n if(error == 0)\n {\n devices++;\n Serial.print("Device found at 0x");\n delay(100);\n Serial.print(address, HEX);\n Serial.println();\n check_i2c_sensor(address);\n }\n else if(error==4)\n {\n Serial.print("Unknow error at 0x");\n delay(100);\n Serial.println(address, HEX);\n }\n }\n\n if(devices == 0) Serial.println("No devices found\\n");\n senseBoxIO.statusNone();\n delay(250);\n break;\n #ifdef ENABLE_WIFI\n case \'2\':\n connectionWiFiTest();\n Serial.println("");\n delay(250);\n break;\n #endif\n #ifdef ENABLE_ETHERNET\n case \'3\':\n connectionEthernetTest();\n Serial.println("");\n delay(250);\n break;\n #endif\n case \'4\':\n Serial.println("Security key:");\n getSecKey();\n Serial.println();\n delay(250);\n break;\n case \'5\':\n Serial.flush();\n NVIC_SystemReset();\n break;\n }\n Serial.flush();\n }\n}\nvoid printMenu()\n{\n Serial.println("senseBox MCU option menu\\nType one of the numbers in the input field above and hit \'Enter\'.");\n delay(100);\n Serial.println(" 1 - Find connected devices");\n #ifdef ENABLE_WIFI\n delay(100);\n Serial.println(" 2 - Test connection to openSenseMap (WiFi on XBee1)");\n #endif\n #ifdef ENABLE_ETHERNET\n delay(100);\n Serial.println(" 3 - Test connection to openSenseMap (Ethernet on XBee1)");\n #endif\n delay(100);\n Serial.println(" 4 - Get security key\\n");\n return;\n}\nvoid check_uart_sensor(){\n Serial.println("UART/Serial Port:");\n SDS011 sds1(Serial1);\n SDS011 sds2(Serial2);\n float pm10,pm25;\n int sds_error;\n sds_error = sds1.read(&pm25,&pm10);\n if (!sds_error)\n {\n Serial.println("SDS011 dust particle sensor found at serial port #1.");\n }\n else\n {\n sds_error = sds2.read(&pm25,&pm10);\n if (!sds_error)\n {\n Serial.println("SDS011 dust particle sensor found at serial port #2.");\n return;\n }\n }\n Serial.println("No device found.");\n}\nvoid check_i2c_sensor(byte address)\n{\n float t=0, h=0, p=0, a=0;\n unsigned int u=0;\n unsigned long l=0;\n Adafruit_BMP280 bmp280;\n Adafruit_BME280 bme280;\n Adafruit_BME680 bme680;\n Adafruit_HDC1000 hdc;\n if((address == 0) || (address > 127))\n {\n return;\n }\n switch(address)\n {\n case 0x29: //TSL45315\n Serial.println("--- TSL45315");\n Wire.beginTransmission(address);\n Wire.write(0x80|0x00); //control\n Wire.write(0x03);\n Wire.endTransmission();\n Wire.beginTransmission(address);\n Wire.write(0x80|0x01); //config\n Wire.write(0x02); //M=4 T=100ms\n Wire.endTransmission();\n delay(120);\n Wire.beginTransmission(address);\n Wire.write(0x80|0x04); //data low\n Wire.endTransmission();\n Wire.requestFrom((uint8_t)address, (uint8_t)2);\n delay(1);\n u |= (Wire.read()<<8);\n u |= (Wire.read()<<8);\n l = u * 4;\n Serial.print("Lux ");\n Serial.println(l, DEC);\n break;\n case 0x38: //VEML6070\n //case 0x39:\n Serial.println("--- VEML6070 (0x38+0x39)");\n Wire.beginTransmission(address);\n Wire.write((0x1<<2) | 0x02); //Integration Time 1\n Wire.endTransmission();\n delay(120);\n Wire.requestFrom((uint8_t)(address+1), (uint8_t)1); //MSB\n delay(1);\n u |= (Wire.read()<<8);\n Wire.requestFrom((uint8_t)(address+0), (uint8_t)1); //LSB\n delay(1);\n u |= (Wire.read()<<0);\n Serial.print("UV ");\n Serial.println(u, DEC);\n break;\n case 0x40: //HDC100X\n case 0x41:\n //case 0x42:\n case 0x43:\n Serial.println("--- HDC100X");\n hdc.begin(address);\n t = hdc.readTemperature();\n h = hdc.readHumidity();\n Serial.print("Temp ");\n Serial.print(t, DEC);\n Serial.println(" *C");\n Serial.print("Humi ");\n Serial.print(h, DEC);\n Serial.println(" %");\n break;\n case 0x76: //BMP280 or BME280 or BME680\n case 0x77:\n if(bmp280.begin(address) != 0)\n {\n Serial.println("--- BMP280");\n delay(100);\n t = bmp280.readTemperature();\n p = bmp280.readPressure();\n a = bmp280.readAltitude(1013.25); //1013.25 = sea level pressure\n }\n else if(bme280.begin(address) != 0)\n {\n Serial.println("--- BME280");\n delay(100);\n t = bme280.readTemperature();\n p = bme280.readPressure();\n a = bme280.readAltitude(1013.25); //1013.25 = sea level pressure\n h = bme280.readHumidity();\n }\n else if(bme680.begin(address) != 0)\n {\n Serial.println("--- BME680");\n delay(100);\n bme680.performReading();\n t = bme680.temperature;\n p = bme680.pressure;\n a = bme680.readAltitude(1013.25); //1013.25 = sea level pressure\n h = bme680.humidity;\n u = bme680.gas_resistance / 1000.0;\n }\n else\n {\n Wire.beginTransmission(address);\n Wire.write(0xD0); //chip id\n Wire.endTransmission();\n Wire.requestFrom(address, (byte)1);\n delay(1);\n u = Wire.read();\n if(u == 0x58) //BMP280\n {\n Serial.println("--- BMP280");\n }\n else if(u == 0x60) //BME280\n {\n Serial.println("--- BME280");\n }\n else if(u == 0x61) //BME680\n {\n Serial.println("--- BME680");\n }\n }\n Serial.print("Temp ");\n Serial.print(t, DEC);\n Serial.println(" *C");\n Serial.print("Pres ");\n Serial.print(p/100.0, DEC);\n Serial.println(" hPa");\n Serial.print("Alti ");\n Serial.print(a, DEC);\n Serial.println(" m");\n if(h != 0)\n {\n Serial.print("Humi ");\n Serial.print(h, DEC);\n Serial.println(" %");\n }\n if(u != 0)\n {\n Serial.print("Gas ");\n Serial.print(u, DEC);\n Serial.println(" kOhm");\n }\n break;\n case 0x42: //CAM-M8Q\n Serial.println("--- CAM-M8Q");\n break;\n case 0x50: //24LCxxx EEPROM\n Serial.println("--- 24LCxxx");\n break;\n case 0x60: //ATECCx08\n Serial.println("--- ATECCx08");\n break;\n case 0x68: //RV8523\n Serial.println("--- RV8523");\n break;\n }\n delay(250); //wait 250ms\n}\nvoid getSecKey()\n{\n Wire1.begin();\n // init ATECC\n write(CMD_RESET, 0x00); // reset\n delay(100); // wait 100ms\n // read config zone\n byte buf[64]; // buffer\n buf[0] = 5+2; // length: data + 2 crc bytes\n buf[1] = ECC_READ; // cmd\n buf[2] = ECC_ZONE_CFG|ECC_ZONE_CNT_FLAG; // param 1\n buf[3] = 0x00; // addr lsb\n buf[4] = 0x00; // addr msb\n //buf[5] = 0x00; // crc\n //buf[6] = 0x00; // crc\n calc_crc(buf, buf[0]-2, &buf[5]); // calc crc\n write(CMD_SEND, buf, buf[0]); // send cmd\n delay(10); // wait 10ms\n read(buf, sizeof(buf)); // read response\n Serial.print("0");\n Serial.print(buf[1], HEX); Serial.print(" ");\n Serial.print(buf[2], HEX); Serial.print(" ");\n Serial.print(buf[3], HEX); Serial.print(" ");\n Serial.print(buf[4], HEX); Serial.print(" ");\n Serial.print(buf[ 9], HEX); Serial.print(" ");\n Serial.print(buf[10], HEX); Serial.print(" ");\n Serial.print(buf[11], HEX); Serial.print(" ");\n Serial.print(buf[12], HEX); Serial.print(" ");\n Serial.print(buf[13], HEX); Serial.print(" ");\n Serial.println("");\n}\nvoid read(byte *data, byte max_len)\n{\n byte len;\n Wire1.requestFrom(I2C_ATECC, 1); // request length\n while(Wire1.available() == 0); // wait for data bytes\n len = Wire1.read();\n *data++ = len;\n if(len)\n {\n Wire1.requestFrom(I2C_ATECC, len); // request x bytes\n while(Wire1.available() == 0); // wait for data bytes\n delay(10); // wait 10ms\n for(byte i = 0; (i < len) && (i < max_len); i++)\n {\n *data++ = Wire1.read(); // read data byte\n }\n }\n}\nvoid write(byte reg, byte *data, byte len)\n{\n Wire1.beginTransmission(I2C_ATECC); // start transmission\n Wire1.write(reg); // write register byte\n for(; len != 0; len--)\n {\n Wire1.write(*data++); // write data byte\n }\n Wire1.endTransmission(); // stop transmission\n}\nvoid write(byte reg, byte data)\n{\n Wire1.beginTransmission(I2C_ATECC); // start transmission\n Wire1.write(reg); // write register byte\n Wire1.write(data); // write data byte\n Wire1.endTransmission(); // stop transmission\n}\nvoid calc_crc(byte *data, byte len, byte *crc)\n{\n uint8_t i, shift_reg, data_bit, crc_bit;\n uint16_t crc_reg = 0;\n uint16_t polynom = 0x8005;\n for(i = 0; i < len; i++)\n {\n for(shift_reg = 0x01; shift_reg > 0x00; shift_reg <<= 1)\n {\n data_bit = (data[i] & shift_reg) ? 1 : 0;\n crc_bit = crc_reg >> 15;\n crc_reg <<= 1;\n if(data_bit != crc_bit)\n {\n crc_reg ^= polynom;\n }\n }\n }\n crc[0] = (byte)(crc_reg & 0x00FF);\n crc[1] = (byte)(crc_reg >> 8);\n}\nvoid connectionWiFiTest(){\n #ifdef ENABLE_WIFI\n if (WiFi.status() == WL_NO_SHIELD)\n {\n Serial.println("WiFi bee not present");\n return;\n }\n Serial.println("Check WiFi firmware:");\n Serial.println("====================");\n // Print firmware version on the shield\n String fv = WiFi.firmwareVersion();\n String latestFv;\n Serial.print("Firmware version installed: ");\n Serial.println(fv);\n\n if (REV(GET_CHIPID()) >= REV_3A0) {\n // model B\n latestFv = WIFI_FIRMWARE_LATEST_MODEL_B;\n } else {\n // model A\n latestFv = WIFI_FIRMWARE_LATEST_MODEL_A;\n }\n\n // Print required firmware version\n Serial.print("Latest firmware version available : ");\n Serial.println(latestFv);\n\n // Check if the latest version is installed\n Serial.println();\n if (fv == latestFv || fv == "19.5.2") {\n Serial.println("Check result: PASSED");\n } else {\n Serial.println("Check result: NOT PASSED");\n Serial.println(" - The firmware version on the shield do not match the");\n Serial.println(" version required by the library, you may experience");\n Serial.println(" issues or failures.");\n Serial.println(" - Update the firmware at least to version 19.5.2");\n }\n\n Serial.println();\n Serial.println("Check internet connectivity:");\n Serial.println("============================");\n\n if (WiFi.status() != WL_CONNECTED) {\n Serial.print("Connecting to WiFi...");\n delay(1000); // wait 1s\n WiFi.begin(ssid, pass);\n delay(5000); // wait 5s\n }\n if (WiFi.status() == WL_CONNECTED) Serial.println("connected!");\n else\n {\n Serial.println("failed! Please check SSID and password.");\n return;\n }\n for (uint8_t timeout = 2; timeout != 0; timeout--)\n {\n Serial.print("Calling openSenseMap server...");\n if (client.connect(server, 80))\n {\n Serial.println("connected!");\n // Make a HTTP request:\n client.println("GET / HTTP/1.1");\n client.print("Host: ");\n client.println(server);\n client.println("Connection: close");\n client.println();\n }\n break;\n }\n if(client.connected())\n {\n // wait for server response\n Serial.println("Server response:\\n");\n while (!client.available())\n {\n delay(1);\n }\n // read server response\n while (client.available())\n {\n char c = client.read();\n Serial.write(c);\n }\n Serial.print("\\n");\n Serial.println("Disconnecting from server.");\n client.flush();\n client.stop();\n }else Serial.println("failed after 3 trys!");\n Serial.println("Disconnecting from WiFi.");\n WiFi.disconnect();\n #endif\n}\n\nvoid connectionEthernetTest() {\n #ifdef ENABLE_ETHERNET\n Ethernet.init(PIN_XB1_CS);\n Serial.println("Trying to initialize DHCP...");\n if (Ethernet.begin(mac) == 0) {\n Serial.println("Failed to configure Ethernet using DHCP");\n // start the Ethernet connection using a fixed IP address and DNS server:\n Serial.println("Trying Ethernet connection using a fixed IP address and DNS server");\n Ethernet.begin(mac, myIp, myDns, mySubnet);\n } else {\n // print your local IP address:\n Serial.println("DHCP is working.");\n Serial.print("My IP address: ");\n for (byte thisByte = 0; thisByte < 4; thisByte++) {\n // print the value of each byte of the IP address:\n Serial.print(Ethernet.localIP()[thisByte], DEC);\n Serial.print(".");\n }\n Serial.println();\n }\n for (uint8_t timeout = 2; timeout != 0; timeout--)\n {\n Serial.print("Calling openSenseMap server...");\n if (ethernetClient.connect(server, 80))\n {\n Serial.println("connected!");\n // Make a HTTP request:\n ethernetClient.println("GET / HTTP/1.1");\n ethernetClient.print("Host: ");\n ethernetClient.println(server);\n ethernetClient.println("Connection: close");\n ethernetClient.println();\n }\n break;\n }\n if(ethernetClient.connected())\n {\n // wait for server response\n Serial.println("Server response:\\n");\n while (!ethernetClient.available())\n {\n delay(1);\n }\n // read server response\n while (ethernetClient.available())\n {\n char c = ethernetClient.read();\n Serial.write(c);\n }\n Serial.print("\\n");\n Serial.println("Disconnecting from server.");\n ethernetClient.flush();\n ethernetClient.stop();\n }\n else Serial.println("failed after 3 trys!");\n #endif\n}', + }, + { + name: "ArduinoBearSSL", + description: "BearSSL is a TLS/SSL library for Arduino", + code: "", + }, + ]; +}; diff --git a/src/data/versions.js b/src/data/versions.js new file mode 100644 index 0000000..77f7651 --- /dev/null +++ b/src/data/versions.js @@ -0,0 +1,102 @@ +export const LibraryVersions = () => { + return [ + { + version: "1.4.2", + library: "sensebox/SenseBoxMCU-Lib", + link: "https://github.com/sensebox/SenseBoxMCU-Lib", + }, + { + version: "1.0.12", + library: "sparkfun/SparkFun_SCD30_Arduino_Library", + }, + { version: "1.2.3", library: "adafruit/Adafruit-GFX-Library" }, + { + version: "2.1.2", + library: "adafruit/Adafruit_BME280_Library", + }, + { + version: "2.1.0", + library: "adafruit/Adafruit_BMP280_Library", + }, + { + version: "1.1.1", + library: "adafruit/Adafruit_BME680", + }, + { + version: "2.0.1", + library: "adafruit/Adafruit_BMP3XX", + }, + { + version: "2.0.0", + library: "adafruit/Adafruit_HDC1000_Library", + }, + { + version: "1.7.1", + library: "adafruit/Adafruit_BusIO", + }, + { + version: "1.0.6", + library: "adafruit/Adafruit_NeoPixel", + }, + { + version: "1.1.2", + library: "adafruit/Adafruit_SSD1306", + }, + { + version: "1.0.2", + library: "adafruit/Adafruit_Sensor", + }, + { + version: "3.8.0", + library: "milesburton/Arduino-Temperature-Control-Library", + }, + { + version: "1.5.0", + library: "arduino-libraries/ArduinoBearSSL", + }, + { + version: "1.3.4", + library: "arduino-libraries/ArduinoECCX08", + }, + { + version: "2.0.0", + library: "arduino-libraries/ArduinoECCX08", //todo + }, + { + version: "0.7.1", + library: "cmaglie/FlashStorage", + }, + { + version: "1.5.1", + library: "matthijskooijman/arduino-lmic", + }, + { + version: "3.0.1", + library: "thesolarnomad/lora-serialization ", + }, + { + version: "todo", + library: "TSL45xxx", + }, + { + version: "2.3.4", + library: "teensy/td_libs_OneWire.html", + }, + { + version: "1.0.0", + library: "watterott/Arduino-Libs/tree/master/RV8523", + }, + { + version: "1.0.0", + library: "sensebox/SDS011-select-serial ", + }, + { + version: "1.0.0", + library: "Lucas-Steinmann/SSD1306-Plot-Library", + }, + { + version: "1.0.0", + library: "senseBoxIO", + }, + ]; +}; diff --git a/yarn.lock b/yarn.lock index 7ed0fee..f31665f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1542,6 +1542,21 @@ "prop-types" "^15.7.2" "react-is" "^16.8.0" +"@monaco-editor/loader@^1.2.0": + "integrity" "sha512-cJVCG/T/KxXgzYnjKqyAgsKDbH9mGLjcXxN6AmwumBwa2rVFkwvGcUj1RJtD0ko4XqLqJxwqsN/Z/KURB5f1OQ==" + "resolved" "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "state-local" "^1.0.6" + +"@monaco-editor/react@^4.3.1": + "integrity" "sha512-f+0BK1PP/W5I50hHHmwf11+Ea92E5H1VZXs+wvKplWUWOfyMa1VVwqkJrXjRvbcqHL+XdIGYWhWNdi4McEvnZg==" + "resolved" "https://registry.npmjs.org/@monaco-editor/react/-/react-4.3.1.tgz" + "version" "4.3.1" + dependencies: + "@monaco-editor/loader" "^1.2.0" + "prop-types" "^15.7.2" + "@nodelib/fs.scandir@2.1.4": "integrity" "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==" "resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz" @@ -8224,6 +8239,11 @@ "resolved" "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz" "version" "2.29.0" +"monaco-editor@>= 0.21.0 < 1", "monaco-editor@>= 0.25.0 < 1": + "integrity" "sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==" + "resolved" "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz" + "version" "0.31.1" + "move-concurrently@^1.0.1": "integrity" "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=" "resolved" "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz" @@ -10032,7 +10052,7 @@ "strip-ansi" "6.0.0" "text-table" "0.2.0" -"react-dom@^17.0.0", "react-dom@^17.0.2": +"react-dom@^16.8.0 || ^17.0.0", "react-dom@^17.0.0", "react-dom@^17.0.2": "integrity" "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==" "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" "version" "17.0.2" @@ -10211,7 +10231,7 @@ "loose-envify" "^1.4.0" "prop-types" "^15.6.2" -"react@^16.8.3 || ^17", "react@^17.0.0", "react@^17.0.2", "react@>= 16", "react@17.0.2": +"react@^16.8.0 || ^17.0.0", "react@^16.8.3 || ^17", "react@^17.0.0", "react@^17.0.2", "react@>= 16", "react@17.0.2": "integrity" "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==" "resolved" "https://registry.npmjs.org/react/-/react-17.0.2.tgz" "version" "17.0.2" @@ -11408,6 +11428,11 @@ "resolved" "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz" "version" "1.2.0" +"state-local@^1.0.6": + "integrity" "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" + "resolved" "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz" + "version" "1.0.7" + "static-extend@^0.1.1": "integrity" "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=" "resolved" "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz"