From 8e2deed1d5716b0a4af4cc5b1241858b2ca53d19 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 5 Nov 2020 11:05:14 +0100 Subject: [PATCH 1/3] init implementation of procedures --- src/components/Blockly/blocks/lists.js | 3 +- src/components/Blockly/blocks/procedures.js | 1178 ++++++++++++++++- .../Blockly/blocks/sensebox-display.js | 2 - .../Blockly/blocks/sensebox-osem.js | 3 - .../Blockly/blocks/sensebox-sensors.js | 12 +- src/components/Blockly/generator/generator.js | 2 +- .../Blockly/generator/procedures.js | 94 ++ .../Blockly/generator/sensebox-sensors.js | 16 +- src/components/Blockly/helpers/types.js | 2 +- src/components/Blockly/msg/de.js | 2 + src/components/Blockly/msg/en.js | 3 + src/components/Blockly/toolbox/Toolbox.js | 1 + src/components/Gallery/GalleryHome.js | 1 - src/components/Home.js | 11 +- src/components/Tutorial/Builder/Builder.js | 2 - src/components/Tutorial/HintTutorialExists.js | 9 +- 16 files changed, 1299 insertions(+), 42 deletions(-) diff --git a/src/components/Blockly/blocks/lists.js b/src/components/Blockly/blocks/lists.js index 2d33472..12948ae 100644 --- a/src/components/Blockly/blocks/lists.js +++ b/src/components/Blockly/blocks/lists.js @@ -1,5 +1,4 @@ -import Blockly, { FieldDropdown } from 'blockly/core'; -import { selectedBoard } from '../helpers/board' +import Blockly, { FieldDropdown } from 'blockly/core' import * as Types from '../helpers/types' import { getColour } from '../helpers/colour'; diff --git a/src/components/Blockly/blocks/procedures.js b/src/components/Blockly/blocks/procedures.js index 5bd6214..237e4da 100644 --- a/src/components/Blockly/blocks/procedures.js +++ b/src/components/Blockly/blocks/procedures.js @@ -11,7 +11,7 @@ import * as Blockly from 'blockly/core'; import { getColour } from '../helpers/colour'; - +import * as Types from '../helpers/types'; Blockly.Blocks['arduino_functions'] = { /** @@ -36,3 +36,1179 @@ Blockly.Blocks['arduino_functions'] = { return true; } }; + + + +Blockly.Blocks['procedures_defnoreturn'] = { + /** + * Block for defining a procedure with no return value. + * @this Blockly.Block + */ + init: function () { + const nameField = new Blockly.FieldTextInput('', Blockly.Procedures.rename); + nameField.setSpellcheck(false); + this.appendDummyInput() + .appendField('Create block') + .appendField(nameField, 'NAME') + .appendField('', 'PARAMS'); + this.setMutator(new Blockly.Mutator(['procedures_mutatorarg'])); + if ( + (this.workspace.options.comments || + (this.workspace.options.parentWorkspace && + this.workspace.options.parentWorkspace.options.comments)) && + Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT'] + ) { + this.setCommentText(Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']); + } + this.setColour(getColour().procedures); + this.setTooltip(Blockly.Msg['PROCEDURES_DEFNORETURN_TOOLTIP']); + this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFNORETURN_HELPURL']); + this.arguments_ = []; + this.argumentVarModels_ = []; + this.setStatements_(true); + this.statementConnection_ = null; + }, + /** + * Add or remove the statement block from this function definition. + * @param {boolean} hasStatements True if a statement block is needed. + * @this Blockly.Block + */ + setStatements_: function (hasStatements) { + if (this.hasStatements_ === hasStatements) { + return; + } + if (hasStatements) { + this.appendStatementInput('STACK').appendField( + Blockly.Msg['PROCEDURES_DEFNORETURN_DO'] + ); + if (this.getInput('RETURN')) { + this.moveInputBefore('STACK', 'RETURN'); + } + } else { + this.removeInput('STACK', true); + } + this.hasStatements_ = hasStatements; + }, + /** + * Update the display of parameters for this procedure definition block. + * Display a warning if there are duplicate named parameters. + * @private + * @this Blockly.Block + */ + updateParams_: function () { + // Check for duplicated arguments. + let badArg = false; + const hash = {}; + for (let i = 0; i < this.arguments_.length; i++) { + if (hash['arg_' + this.arguments_[i].toLowerCase()]) { + badArg = true; + break; + } + hash['arg_' + this.arguments_[i].toLowerCase()] = true; + } + if (badArg) { + this.setWarningText(Blockly.Msg['PROCEDURES_DEF_DUPLICATE_WARNING']); + } else { + this.setWarningText(null); + } + // Merge the arguments into a human-readable list. + let paramString = ''; + const paramStringArgs = []; + if (this.argumentVarModels_.length) { + for (let ii = 0; ii < this.argumentVarModels_.length; ii += 1) { + paramStringArgs.push( + ' ' + + this.argumentVarModels_[ii].name + + ' (' + + this.argumentVarModels_[ii].type + + ')' + ); + } + paramString = + Blockly.Msg['PROCEDURES_BEFORE_PARAMS'] + paramStringArgs.join(', '); + } + console.log(paramString); + // The params field is deterministic based on the mutation, + // no need to fire a change event. + Blockly.Events.disable(); + try { + this.setFieldValue(paramString, 'PARAMS'); + } finally { + Blockly.Events.enable(); + } + }, + /** + * Create XML to represent the argument inputs. + * @param {boolean=} opt_paramIds If true include the IDs of the parameter + * quarks. Used by Blockly.Procedures.mutateCallers for reconnection. + * @return {!Element} XML storage element. + * @this Blockly.Block + */ + mutationToDom: function (opt_paramIds) { + const container = document.createElement('mutation'); + if (opt_paramIds) { + container.setAttribute('name', this.getFieldValue('NAME')); + } + for (let i = 0; i < this.argumentVarModels_.length; i++) { + const parameter = document.createElement('arg'); + const argModel = this.argumentVarModels_[i]; + parameter.setAttribute('name', argModel.name); + parameter.setAttribute('type', argModel.type); // NOTES Setting the type of variable here + parameter.setAttribute('varid', argModel.getId()); + if (opt_paramIds && this.paramIds_) { + parameter.setAttribute('paramId', this.paramIds_[i]); + } + container.appendChild(parameter); + } + + if (this.getFieldValue('RETURN TYPE')) { + container.setAttribute('return_type', this.getFieldValue('RETURN TYPE')); + } + + // Save whether the statement input is visible. + if (!this.hasStatements_) { + container.setAttribute('statements', 'false'); + } + return container; + }, + /** + * Parse XML to restore the argument inputs. + * @param {!Element} xmlElement XML storage element. + * @this Blockly.Block + */ + domToMutation: function (xmlElement) { + this.arguments_ = []; + this.argumentVarModels_ = []; + for (let i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { + if (childNode.nodeName.toLowerCase() === 'arg') { + const varName = childNode.getAttribute('name'); + const type = childNode.getAttribute('type'); + // NOTES GETTING THE TYPE OF VARIABLE + const varId = childNode.getAttribute('varid'); + this.arguments_.push(varName); + const variable = Blockly.Variables.getOrCreateVariablePackage( + this.workspace, + varId, + varName, + type + ); + this.argumentVarModels_.push(variable); + } + } + this.updateParams_(); + Blockly.Procedures.mutateCallers(this); + + // Show or hide the statement input. + this.setStatements_(xmlElement.getAttribute('statements') !== 'false'); + }, + /** + * Populate the mutator's dialog with this block's components. + * @param {!Blockly.Workspace} workspace Mutator's workspace. + * @return {!Blockly.Block} Root block in mutator. + * @this Blockly.Block + */ + decompose: function (workspace) { + const containerBlock = workspace.newBlock('procedures_mutatorcontainer'); + containerBlock.initSvg(); + + // Check/uncheck the allow statement box. + if (this.getInput('RETURN')) { + containerBlock.setFieldValue( + this.hasStatements_ ? 'TRUE' : 'FALSE', + 'STATEMENTS' + ); + } else { + containerBlock.getInput('STATEMENT_INPUT').setVisible(false); + } + + // Parameter list. + let connection = containerBlock.getInput('STACK').connection; + for (let i = 0; i < this.arguments_.length; i++) { + const paramBlock = workspace.newBlock('procedures_mutatorarg'); + paramBlock.initSvg(); + paramBlock.setFieldValue(this.arguments_[i], 'NAME'); + paramBlock.setFieldValue(this.argumentVarModels_[i].type, 'TYPE'); + // Trying to add type back + // Store the old location. + paramBlock.oldLocation = i; + connection.connect(paramBlock.previousConnection); + connection = paramBlock.nextConnection; + } + // Initialize procedure's callers with blank IDs. + Blockly.Procedures.mutateCallers(this); + return containerBlock; + }, + /** + * Reconfigure this block based on the mutator dialog's components. + * @param {!Blockly.Block} containerBlock Root block in mutator. + * @this Blockly.Block + */ + compose: function (containerBlock) { + // Parameter list. + this.arguments_ = []; + this.paramIds_ = []; + this.argumentVarModels_ = []; + let paramBlock = containerBlock.getInputTargetBlock('STACK'); + while (paramBlock) { + const varName = paramBlock.getFieldValue('NAME'); + this.arguments_.push(varName); + const variable = this.workspace.getVariable( + varName, + paramBlock.getFieldValue('TYPE') // NOTES SETTING TYPE OF VARIABLE + ); + + this.argumentVarModels_.push(variable); + this.paramIds_.push(paramBlock.id); + paramBlock = + paramBlock.nextConnection && paramBlock.nextConnection.targetBlock(); + } + this.updateParams_(); + Blockly.Procedures.mutateCallers(this); + + // Show/hide the statement input. + let hasStatements = containerBlock.getFieldValue('STATEMENTS'); + if (hasStatements !== null) { + hasStatements = hasStatements === 'TRUE'; + if (this.hasStatements_ !== hasStatements) { + if (hasStatements) { + this.setStatements_(true); + // Restore the stack, if one was saved. + Blockly.Mutator.reconnect(this.statementConnection_, this, 'STACK'); + this.statementConnection_ = null; + } else { + // Save the stack, then disconnect it. + const stackConnection = this.getInput('STACK').connection; + this.statementConnection_ = stackConnection.targetConnection; + if (this.statementConnection_) { + const stackBlock = stackConnection.targetBlock(); + stackBlock.unplug(); + stackBlock.bumpNeighbours_(); + } + this.setStatements_(false); + } + } + } + }, + + /** + * Return the signature of this procedure definition. + * @return {!Array} Tuple containing three elements: + * - the name of the defined procedure, + * - a list of all its arguments, + * - that it DOES NOT have a return value. + * @this Blockly.Block + */ + getProcedureDef: function () { + return [ + this.getFieldValue('NAME'), + this.arguments_, + false, + this.argumentVarModels_ + ]; + }, + /** + * Return all variables referenced by this block. + * @return {!Array.} List of variable names. + * @this Blockly.Block + */ + getVars: function () { + return this.arguments_; + }, + /** + * Return all variables referenced by this block. + * @return {!Array.} List of variable models. + * @this Blockly.Block + */ + getVarModels: function () { + return this.argumentVarModels_; + }, + /** + * Notification that a variable is renaming. + * If the ID matches one of this block's variables, rename it. + * @param {string} oldId ID of variable to rename. + * @param {string} newId ID of new variable. May be the same as oldId, but + * with an updated name. Guaranteed to be the same type as the old + * variable. + * @override + * @this Blockly.Block + */ + renameVarById: function (oldId, newId) { + const oldVariable = this.workspace.getVariableById(oldId); + if (oldVariable.type !== '') { + // Procedure arguments always have the empty type. + return; + } + const oldName = oldVariable.name; + const newVar = this.workspace.getVariableById(newId); + + let change = false; + for (let i = 0; i < this.argumentVarModels_.length; i++) { + if (this.argumentVarModels_[i].getId() === oldId) { + this.arguments_[i] = newVar.name; + this.argumentVarModels_[i] = newVar; + change = true; + } + } + if (change) { + this.displayRenamedVar_(oldName, newVar.name); + } + }, + /** + * Notification that a variable is renaming but keeping the same ID. If the + * variable is in use on this block, rerender to show the new name. + * @param {!Blockly.VariableModel} variable The variable being renamed. + * @package + * @override + * @this Blockly.Block + */ + updateVarName: function (variable) { + let oldName; + const newName = variable.name; + let change = false; + for (let i = 0; i < this.argumentVarModels_.length; i++) { + if (this.argumentVarModels_[i].getId() === variable.getId()) { + oldName = this.arguments_[i]; + this.arguments_[i] = newName; + change = true; + } + } + if (change) { + this.displayRenamedVar_(oldName, newName); + } + }, + /** + * Update the display to reflect a newly renamed argument. + * @param {string} oldName The old display name of the argument. + * @param {string} newName The new display name of the argument. + * @private + */ + displayRenamedVar_: function (oldName, newName) { + this.updateParams_(); + // Update the mutator's variables if the mutator is open. + if (this.mutator.isVisible()) { + const blocks = this.mutator.workspace_.getAllBlocks(false); + for (let i = 0, block; (block = blocks[i]); i++) { + if ( + block.type === 'procedures_mutatorarg' && + Blockly.Names.equals(oldName, block.getFieldValue('NAME')) + ) { + block.setFieldValue(newName, 'NAME'); + } + } + } + }, + /** + * Add custom menu options to this block's context menu. + * @param {!Array} options List of menu options to add to. + * @this Blockly.Block + */ + customContextMenu: function (options) { + if (this.isInFlyout) { + return; + } + // Add option to create caller. + const option = { enabled: true }; + const name = this.getFieldValue('NAME'); + option.text = Blockly.Msg['PROCEDURES_CREATE_DO'].replace('%1', name); + const xmlMutation = document.createElement('mutation'); + xmlMutation.setAttribute('name', name); + for (let i = 0; i < this.arguments_.length; i++) { + const xmlArg = document.createElement('arg'); + xmlArg.setAttribute('name', this.arguments_[i]); + xmlArg.setAttribute('type', this.argumentVarModels_[i].type); // TYPE FIGURED OUT + xmlMutation.appendChild(xmlArg); + } + const xmlBlock = document.createElement('block'); + xmlBlock.setAttribute('type', this.callType_); + xmlBlock.appendChild(xmlMutation); + option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + options.push(option); + + // Add options to create getters for each parameter. + if (!this.isCollapsed()) { + for (let i = 0; i < this.argumentVarModels_.length; i++) { + const option = { enabled: true }; + const argVar = this.argumentVarModels_[i]; + const name = argVar.name; + option.text = Blockly.Msg['VARIABLES_SET_CREATE_GET'].replace( + '%1', + name + ); + + const xmlField = Blockly.Variables.generateVariableFieldDom(argVar); + const xmlBlock = document.createElement('block'); + xmlBlock.setAttribute('type', 'variables_get'); + xmlBlock.appendChild(xmlField); + option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + options.push(option); + } + } + }, + callType_: 'procedures_callnoreturn' +}; + +Blockly.Blocks['procedures_defreturn'] = { + /** + * Block for defining a procedure with a return value. + * @this Blockly.Block + */ + init: function () { + const returnTypeField = new Blockly.FieldDropdown( + [ + ['Number', 'Number'], + ['String', 'String'], + ['Boolean', 'Boolean'], + ['List Number', 'List Number'], + ['List String', 'List String'], + ['List Boolean', 'List Boolean'] + ], + this.updateReturnType.bind(this) + ); + + const nameField = new Blockly.FieldTextInput('', Blockly.Procedures.rename); + nameField.setSpellcheck(false); + this.appendDummyInput() + .appendField('Create block') + .appendField(nameField, 'NAME') + .appendField('', 'PARAMS') + .appendField('that returns: ') + .appendField(returnTypeField, 'RETURN TYPE'); + + this.appendValueInput('RETURN') + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']); + this.setMutator(new Blockly.Mutator(['procedures_mutatorarg'])); + if ( + (this.workspace.options.comments || + (this.workspace.options.parentWorkspace && + this.workspace.options.parentWorkspace.options.comments)) && + Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT'] + ) { + this.setCommentText(Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']); + } + this.setColour(getColour().procedures); + this.setTooltip(Blockly.Msg['PROCEDURES_DEFRETURN_TOOLTIP']); + this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFRETURN_HELPURL']); + this.arguments_ = []; + this.argumentVarModels_ = []; + this.setStatements_(true); + this.statementConnection_ = null; + // Start the return the type off as a number. + this.updateReturnType('Number'); + }, + setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_, + updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_, + mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom, + domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation, + decompose: Blockly.Blocks['procedures_defnoreturn'].decompose, + compose: Blockly.Blocks['procedures_defnoreturn'].compose, + /** + * Return the signature of this procedure definition. + * @return {!Array} Tuple containing three elements: + * - the name of the defined procedure, + * - a list of all its arguments, + * - that it DOES have a return value. + * @this Blockly.Block + */ + getProcedureDef: function () { + return [ + this.getFieldValue('NAME'), + this.arguments_, + true, + this.argumentVarModels_ + ]; + }, + + /** + * This will update all update the return type on the block and will update all the callers for the i + * + * @param returnType The type that is being checked + */ + updateReturnType: function (returnType) { + const returnInput = this.getInput('RETURN'); + + if (!returnInput) { + return; + } + + returnInput.setCheck([returnType]); + + if ( + returnInput.connection && + returnInput.connection.getCheck().indexOf(returnType) === -1 + ) { + returnInput.connection.disconnect(); + returnInput.connection.bumpNeighbours_(); + } + + const callers = Blockly.Procedures.getCallers( + this.getFieldValue('NAME'), + this.workspace + ); + + for (let i = 0, caller; (caller = callers[i]); i++) { + caller.setOutput(true, returnType); + const parent = caller.getParent(); + if (parent) { + const input = parent.getInputWithBlock(caller); + if ( + input.connection.getCheck() !== null && + input.connection.getCheck().indexOf(returnType) === -1 + ) { + input.connection.disconnect(); + input.connection.bumpNeighbours_(); + } + } + } + }, + getVars: Blockly.Blocks['procedures_defnoreturn'].getVars, + getVarModels: Blockly.Blocks['procedures_defnoreturn'].getVarModels, + renameVarById: Blockly.Blocks['procedures_defnoreturn'].renameVarById, + updateVarName: Blockly.Blocks['procedures_defnoreturn'].updateVarName, + displayRenamedVar_: + Blockly.Blocks['procedures_defnoreturn'].displayRenamedVar_, + customContextMenu: Blockly.Blocks['procedures_defnoreturn'].customContextMenu, + callType_: 'procedures_callreturn' +}; + +Blockly.Blocks['procedures_mutatorcontainer'] = { + /** + * Mutator block for procedure container. + * @this Blockly.Block + */ + init: function () { + this.appendDummyInput().appendField( + Blockly.Msg['PROCEDURES_MUTATORCONTAINER_TITLE'] + ); + this.appendStatementInput('STACK'); + this.appendDummyInput('STATEMENT_INPUT') + .appendField(Blockly.Msg['PROCEDURES_ALLOW_STATEMENTS']) + .appendField(new Blockly.FieldCheckbox('TRUE'), 'STATEMENTS'); + this.setColour(getColour().procedures); + this.setTooltip(Blockly.Msg['PROCEDURES_MUTATORCONTAINER_TOOLTIP']); + this.contextMenu = false; + } +}; + +let parameterCounter = 1; + +Blockly.Blocks['procedures_mutatorarg'] = { + /** + * Mutator block for procedure argument. + * @this Blockly.Block + */ + init: function () { + const paramName = 'param' + parameterCounter.toString(); + + // This is for dialog box that get's opened + // It has a flyout menu with the default variable + // This will set the name of that default variable + if (this.workspace.flyout_) { + this.workspace.flyout_.workspace_ + .getAllBlocks()[0] + .setFieldValue(paramName, 'NAME'); + } + + const typeField = new Blockly.FieldDropdown( + [ + ['Number', 'Number'], + ['String', 'String'], + ['Boolean', 'Boolean'], + ['List Number', 'List Number'], + ['List String', 'List String'], + ['List Boolean', 'List Boolean'] + ], + this.validatorType_.bind(this) + ); + const nameField = new Blockly.FieldTextInput( + paramName, + this.validateName.bind(this) + ); + // Hack: override showEditor to do just a little bit more work. + // We don't have a good place to hook into the start of a text edit. + nameField.oldShowEditorFn_ = nameField.showEditor_; + const newShowEditorFn = function () { + this.createdVariables_ = []; + this.oldShowEditorFn_(); + }; + + nameField.showEditor_ = newShowEditorFn; + + this.appendDummyInput() + .appendField(Blockly.Msg['PROCEDURES_MUTATORARG_TITLE']) + .appendField(nameField, 'NAME') + .appendField(typeField, 'TYPE'); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setColour(getColour().procedures); + this.setTooltip(Blockly.Msg['PROCEDURES_MUTATORARG_TOOLTIP']); + this.contextMenu = false; + + // Create the default variable when we drag the block in from the flyout. + // Have to do this after installing the field on the block. + nameField.onFinishEditing_ = this.deleteIntermediateVars_.bind(this); + // Create an empty list so onFinishEditing_ has something to look at, even + // though the editor was never opened. + this.createdVariables_ = []; + nameField.onFinishEditing_(paramName); + parameterCounter += 1; + }, + /** + * Obtain a valid name for the procedure argument. Create a variable if + * necessary. + * Merge runs of whitespace. Strip leading and trailing whitespace. + * Beyond this, all names are legal. + * @param {string} varName User-supplied name. + * @return {?string} Valid name, or null if a name was not specified. + * @private + */ + validateName: function (varName) { + this.validateVariable(varName, this.getFieldValue('TYPE')); + + varName = varName.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, ''); + + if (!varName) { + return null; + } + + const blocks = this.workspace.getAllBlocks(); + + for (let i = 0; i < blocks.length; i += 1) { + if (blocks[i].id === this.id) { + continue; + } + if (blocks[i].getFieldValue('NAME') === varName) { + return null; + } + } + + return varName; + }, + + /** + * This will add a variable to the list variables in the main workspace + * @param varType + * @private + */ + validatorType_: function (varType) { + this.validateVariable(this.getFieldValue('NAME'), varType); + + return varType; + }, + + /** + * This will validate the variable being used + * + * @param varName + * @param varType + * @return {*} + */ + validateVariable: function (varName, varType) { + varName = varName.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, ''); + + if (!varName) { + return null; + } + + const outerWs = Blockly.mainWorkspace; + let model = outerWs.getVariable(varName, varType); + // CHANGING VARIABLE NAME TO REFLECT TYPE + if (model && model.name !== varName) { + // Rename the variable (case change) + outerWs.renameVarById(model.getId(), varName); + } + + if (!model) { + model = outerWs.createVariable(varName, varType); + // CHANGING VARIABLE NAME TO REFLECT TYPE + if (model && this.createdVariables_) { + this.createdVariables_.push(model); + } + } + }, + + /** + * Called when focusing away from the text field. + * Deletes all variables that were created as the user typed their intended + * variable name. + * @private + */ + deleteIntermediateVars_: function (varName, varType) { + varName = varName || this.getFieldValue('NAME'); + varType = varType || this.getFieldValue('TYPE'); + + const outerWs = Blockly.mainWorkspace; + if (!outerWs) { + return; + } + for (let i = 0; i < this.createdVariables_.length; i++) { + const model = this.createdVariables_[i]; + if (model.name !== varName || model.type !== varType) { + // DELETE CRITERIA NEEDS TO CHANGE + outerWs.deleteVariableById(model.getId()); + } + } + } +}; + +Blockly.Blocks['procedures_callnoreturn'] = { + /** + * Block for calling a procedure with no return value. + * @this Blockly.Block + */ + init: function () { + this.appendDummyInput('TOPROW').appendField(this.id, 'NAME'); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setColour(getColour().procedures); + // Tooltip is set in renameProcedure. + this.setHelpUrl(Blockly.Msg['PROCEDURES_CALLNORETURN_HELPURL']); + this.arguments_ = []; + this.argumentVarModels_ = []; + this.quarkConnections_ = {}; + this.quarkIds_ = null; + this.previousDisabledState_ = false; + this.returnType = null; + }, + + /** + * Returns the name of the procedure this block calls. + * @return {string} Procedure name. + * @this Blockly.Block + */ + getProcedureCall: function () { + // The NAME field is guaranteed to exist, null will never be returned. + return /** @type {string} */ this.getFieldValue('NAME'); + }, + /** + * Notification that a procedure is renaming. + * If the name matches this block's procedure, rename it. + * @param {string} oldName Previous name of procedure. + * @param {string} newName Renamed procedure. + * @this Blockly.Block + */ + renameProcedure: function (oldName, newName) { + if (Blockly.Names.equals(oldName, this.getProcedureCall())) { + this.setFieldValue(newName, 'NAME'); + const baseMsg = this.outputConnection + ? Blockly.Msg['PROCEDURES_CALLRETURN_TOOLTIP'] + : Blockly.Msg['PROCEDURES_CALLNORETURN_TOOLTIP']; + this.setTooltip(baseMsg.replace('%1', newName)); + } + }, + /** + * Notification that the procedure's parameters have changed. + * @param {!Array.} paramNames New param names, e.g. ['x', 'y', 'z']. + * @param {!Array.} paramIds IDs of params (consistent for each + * parameter through the life of a mutator, regardless of param renaming), + * e.g. ['piua', 'f8b_', 'oi.o']. + * @param {!Array.} types like Number, String, etc + * @private + * @this Blockly.Block + */ + setProcedureParameters_: function (paramNames, paramIds, types) { + // Data structures: + // this.arguments = ['x', 'y'] + // Existing param names. + // this.quarkConnections_ {piua: null, f8b_: Blockly.Connection} + // Look-up of paramIds to connections plugged into the call block. + // this.quarkIds_ = ['piua', 'f8b_'] + // Existing param IDs. + // Note that quarkConnections_ may include IDs that no longer exist, but + // which might reappear if a param is reattached in the mutator. + const defBlock = Blockly.Procedures.getDefinition( + this.getProcedureCall(), + this.workspace + ); + const mutatorOpen = + defBlock && defBlock.mutator && defBlock.mutator.isVisible(); + if (!mutatorOpen) { + this.quarkConnections_ = {}; + this.quarkIds_ = null; + } + if (!paramIds) { + // Reset the quarks (a mutator is about to open). + return; + } + + const oldTypes = this.argumentVarModels_ + ? this.argumentVarModels_.map(function (argModel) { + return argModel.type; + }) + : []; + + // Test arguments (arrays of strings) for changes. '\n' is not a valid + // argument name character, so it is a valid delimiter here. + if ( + paramNames.join('\n') === this.arguments_.join('\n') && + oldTypes.join(',') === types.join(',') + ) { + // No change. + this.quarkIds_ = paramIds; + return; + } + if (paramIds.length !== paramNames.length) { + throw RangeError('paramNames and paramIds must be the same length.'); + } + this.setCollapsed(false); + if (!this.quarkIds_) { + // Initialize tracking for this block. + this.quarkConnections_ = {}; + this.quarkIds_ = []; + } + // Switch off rendering while the block is rebuilt. + const savedRendered = this.rendered; + this.rendered = false; + // Update the quarkConnections_ with existing connections. + for (let i = 0; i < this.arguments_.length; i++) { + const input = this.getInput('ARG' + i); + if (input) { + const connection = input.connection.targetConnection; + this.quarkConnections_[this.quarkIds_[i]] = connection; + if ( + mutatorOpen && + connection && + paramIds.indexOf(this.quarkIds_[i]) === -1 + ) { + // This connection should no longer be attached to this block. + connection.disconnect(); + connection.getSourceBlock().bumpNeighbours_(); + } + } + } + // Rebuild the block's arguments. + this.arguments_ = [].concat(paramNames); + // And rebuild the argument model list. + this.argumentVarModels_ = []; + for (let i = 0; i < this.arguments_.length; i++) { + const variable = Blockly.Variables.getOrCreateVariablePackage( + this.workspace, + null, + this.arguments_[i], + types[i] + ); + this.argumentVarModels_.push(variable); + } + + this.updateShape_(); + this.quarkIds_ = paramIds; + // Reconnect any child blocks. + if (this.quarkIds_) { + for (let i = 0; i < this.arguments_.length; i++) { + const quarkId = this.quarkIds_[i]; + if (quarkId in this.quarkConnections_) { + const connection = this.quarkConnections_[quarkId]; + try { + if (!Blockly.Mutator.reconnect(connection, this, 'ARG' + i)) { + // Block no longer exists or has been attached elsewhere. + delete this.quarkConnections_[quarkId]; + } + } catch (e) { + connection.getSourceBlock().bumpNeighbours_(); + + console.error('DELETED DETACHED ' + e.message); + delete this.quarkConnections_[quarkId]; + } + } + } + } + // Restore rendering and show the changes. + this.rendered = savedRendered; + if (this.rendered) { + this.render(); + } + }, + /** + * Modify this block to have the correct number of arguments. + * @private + * @this Blockly.Block + */ + updateShape_: function () { + let i = 0; + for (i = 0; i < this.arguments_.length; i++) { + let field = this.getField('ARGNAME' + i); + const labelString = + this.argumentVarModels_[i].name + + ' (' + + this.argumentVarModels_[i].type + + ') '; + if (field) { + // Ensure argument name is up to date. + // The argument name field is deterministic based on the mutation, + // no need to fire a change event. + Blockly.Events.disable(); + try { + field.setValue(labelString); + this.getInput('ARG' + i).setCheck([this.argumentVarModels_[i].type]); + } finally { + Blockly.Events.enable(); + } + } else { + // Add new input. + field = new Blockly.FieldLabel(labelString); + const input = this.appendValueInput('ARG' + i) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(field, 'ARGNAME' + i) + .setCheck([this.argumentVarModels_[i].type]); // TESTING CHECK TYPES GOES HERE + input.init(); + } + } + // Remove deleted inputs. + while (this.getInput('ARG' + i)) { + this.removeInput('ARG' + i); + i++; + } + // Add 'with:' if there are parameters, remove otherwise. + const topRow = this.getInput('TOPROW'); + if (topRow) { + if (this.arguments_.length) { + if (!this.getField('WITH')) { + topRow.appendField( + Blockly.Msg['PROCEDURES_CALL_BEFORE_PARAMS'], + 'WITH' + ); + topRow.init(); + } + } else { + if (this.getField('WITH')) { + topRow.removeField('WITH'); + } + } + } + }, + /** + * Create XML to represent the (non-editable) name and arguments. + * @return {!Element} XML storage element. + * @this Blockly.Block + */ + mutationToDom: function () { + const container = document.createElement('mutation'); + container.setAttribute('name', this.getProcedureCall()); + for (let i = 0; i < this.arguments_.length; i++) { + const parameter = document.createElement('arg'); + parameter.setAttribute('name', this.arguments_[i]); + parameter.setAttribute('type', this.argumentVarModels_[i].type); + // TESTING ARGUMENTS NEED TO SET IN XML + container.appendChild(parameter); + } + + if (this.returnType) { + container.setAttribute('return_type', this.returnType); + } + return container; + }, + /** + * Parse XML to restore the (non-editable) name and parameters. + * @param {!Element} xmlElement XML storage element. + * @this Blockly.Block + */ + domToMutation: function (xmlElement) { + const name = xmlElement.getAttribute('name'); + this.renameProcedure(this.getProcedureCall(), name); + const args = []; + const paramIds = []; + const types = []; + + for (let i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { + if (childNode.nodeName.toLowerCase() === 'arg') { + var variables = Blockly.mainWorkspace.getAllVariables(); + var varName = childNode.getAttribute('name'); + for (let y = 0; variables.length; y++) { + if (variables[y].name === varName) { + args.push(variables[y].name); + types.push(variables[y].type); + paramIds.push(variables[y].id_); + break; + } + + } + // args.push(childNode.getAttribute('name')); + // types.push(childNode.getAttribute('type')); + // paramIds.push(childNode.getAttribute('paramId')); + } + } + const returnType = xmlElement.getAttribute('return_type'); + + // Sets the output of the block + if (returnType) { + this.setOutput(true, returnType); + this.returnType = returnType; + } + this.setProcedureParameters_(args, paramIds, types); + }, + /** + * Return all variables referenced by this block. + * @return {!Array.} List of variable models. + * @this Blockly.Block + */ + getVarModels: function () { + return this.argumentVarModels_; + }, + /** + * Procedure calls cannot exist without the corresponding procedure + * definition. Enforce this link whenever an event is fired. + * @param {!Blockly.Events.Abstract} event Change event. + * @this Blockly.Block + */ + onchange: function (event) { + if (!this.workspace || this.workspace.isFlyout) { + // Block is deleted or is in a flyout. + return; + } + if (!event.recordUndo) { + // Events not generated by user. Skip handling. + return; + } + + if ( + event.type === Blockly.Events.BLOCK_CREATE && + event.ids.indexOf(this.id) !== -1 + ) { + const typesAndNames = this.argumentVarModels_ + ? this.argumentVarModels_.map(function (arg) { + return { name: arg.name, type: arg.type }; + }) + : []; + + // Look for the case where a procedure call was created (usually through + // paste) and there is no matching definition. In this case, create + // an empty definition block with the correct signature. + const name = this.getProcedureCall(); + let def = Blockly.Procedures.getDefinition(name, this.workspace); + + const defTypesAndNames = def.argumentVarModels_ + ? def.argumentVarModels_.map(function (arg) { + return { name: arg.name, type: arg.type }; + }) + : []; + + if ( + def && + (def.type !== this.defType_ || + JSON.stringify(typesAndNames) !== JSON.stringify(defTypesAndNames)) + ) { + // The signatures don't match. + def = null; + } + if (!def) { + Blockly.Events.setGroup(event.group); + /** + * Create matching definition block. + * + * + * + * + * + * test + * + * + */ + const xml = document.createElement('xml'); + const block = document.createElement('block'); + block.setAttribute('type', this.defType_); + const xy = this.getRelativeToSurfaceXY(); + const x = xy.x + Blockly.SNAP_RADIUS * (this.RTL ? -1 : 1); + const y = xy.y + Blockly.SNAP_RADIUS * 2; + block.setAttribute('x', x); + block.setAttribute('y', y); + const mutation = this.mutationToDom(); + block.appendChild(mutation); + const field = document.createElement('field'); + field.setAttribute('name', 'NAME'); + field.appendChild(document.createTextNode(this.getProcedureCall())); + block.appendChild(field); + xml.appendChild(block); + Blockly.Xml.domToWorkspace(xml, this.workspace); + Blockly.Events.setGroup(false); + } + } else if (event.type === Blockly.Events.BLOCK_DELETE) { + // Look for the case where a procedure definition has been deleted, + // leaving this block (a procedure call) orphaned. In this case, delete + // the orphan. + const name = this.getProcedureCall(); + const def = Blockly.Procedures.getDefinition(name, this.workspace); + if (!def) { + Blockly.Events.setGroup(event.group); + this.dispose(true, false); + Blockly.Events.setGroup(false); + } + } else if ( + event.type === Blockly.Events.CHANGE && + event.element === 'disabled' + ) { + const name = this.getProcedureCall(); + const def = Blockly.Procedures.getDefinition(name, this.workspace); + if (def && def.id === event.blockId) { + // in most cases the old group should be '' + const oldGroup = Blockly.Events.getGroup(); + if (oldGroup) { + // This should only be possible programatically and may indicate a problem + // with event grouping. If you see this message please investigate. If the + // use ends up being valid we may need to reorder events in the undo stack. + console.log( + 'Saw an existing group while responding to a definition change' + ); + } + Blockly.Events.setGroup(event.group); + if (event.newValue) { + this.previousDisabledState_ = this.disabled; + this.setDisabled(true); + } else { + this.setDisabled(this.previousDisabledState_); + } + Blockly.Events.setGroup(oldGroup); + } + } + }, + /** + * Add menu option to find the definition block for this call. + * @param {!Array} options List of menu options to add to. + * @this Blockly.Block + */ + customContextMenu: function (options) { + const option = { enabled: true }; + option.text = Blockly.Msg['PROCEDURES_HIGHLIGHT_DEF']; + const name = this.getProcedureCall(); + const workspace = this.workspace; + option.callback = function () { + const def = Blockly.Procedures.getDefinition(name, workspace); + if (def) { + workspace.centerOnBlock(def.id); + def.select(); + } + }; + options.push(option); + }, + defType_: 'procedures_defnoreturn' +}; + +Blockly.Blocks['procedures_callreturn'] = { + /** + * Block for calling a procedure with a return value. + * @this Blockly.Block + */ + init: function () { + this.appendDummyInput('TOPROW').appendField('', 'NAME'); + this.setOutput(true, 'Number'); // Start off as number + this.setColour(getColour().procedures); + // Tooltip is set in domToMutation. + this.setHelpUrl(Blockly.Msg['PROCEDURES_CALLRETURN_HELPURL']); + this.arguments_ = []; + this.quarkConnections_ = {}; + this.quarkIds_ = null; + this.previousDisabledState_ = false; + }, + + getProcedureCall: Blockly.Blocks['procedures_callnoreturn'].getProcedureCall, + renameProcedure: Blockly.Blocks['procedures_callnoreturn'].renameProcedure, + setProcedureParameters_: + Blockly.Blocks['procedures_callnoreturn'].setProcedureParameters_, + updateShape_: Blockly.Blocks['procedures_callnoreturn'].updateShape_, + mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom, + domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation, + getVarModels: Blockly.Blocks['procedures_callnoreturn'].getVarModels, + onchange: Blockly.Blocks['procedures_callnoreturn'].onchange, + customContextMenu: + Blockly.Blocks['procedures_callnoreturn'].customContextMenu, + defType_: 'procedures_defreturn' +} \ No newline at end of file diff --git a/src/components/Blockly/blocks/sensebox-display.js b/src/components/Blockly/blocks/sensebox-display.js index 8092bff..1e687b9 100644 --- a/src/components/Blockly/blocks/sensebox-display.js +++ b/src/components/Blockly/blocks/sensebox-display.js @@ -50,8 +50,6 @@ Blockly.Blocks['sensebox_display_printDisplay'] = { .setCheck(null); this.setPreviousStatement(true, null); this.setNextStatement(true, null); - let variableName = this.getField('COLOR'); - console.log(variableName.getValue()); this.setTooltip(Blockly.Msg.senseBox_display_printDisplay_tip); this.setHelpUrl('https://sensebox.de/books'); }, diff --git a/src/components/Blockly/blocks/sensebox-osem.js b/src/components/Blockly/blocks/sensebox-osem.js index 0bad7b4..ec8dd5a 100644 --- a/src/components/Blockly/blocks/sensebox-osem.js +++ b/src/components/Blockly/blocks/sensebox-osem.js @@ -103,13 +103,10 @@ Blockly.Blocks['sensebox_send_to_osem'] = { for (var i = 0; i < apiData.sensors.length; i++) { options.push([apiData.sensors[i].title, apiData.sensors[i]._id]); } - console.log(options); - } if (options.length > 1) { var dropdown = options.slice(1) - console.log(dropdown); return dropdown; } else return options; diff --git a/src/components/Blockly/blocks/sensebox-sensors.js b/src/components/Blockly/blocks/sensebox-sensors.js index 2465b24..ad82f54 100644 --- a/src/components/Blockly/blocks/sensebox-sensors.js +++ b/src/components/Blockly/blocks/sensebox-sensors.js @@ -309,7 +309,7 @@ Blockly.Blocks['sensebox_button'] = { Blockly.Blocks['sensebox_scd30'] = { init: function () { - var dropdownOptions = [[Blockly.Msg.senseBox_temp, "temperature"], [Blockly.Msg.senseBox_hum, "humidity"], [Blockly.Msg.senseBox_bme_co2, "CO2"]]; + var dropdownOptions = [[Blockly.Msg.senseBox_scd_co2, "CO2"], [Blockly.Msg.senseBox_temp, "temperature"], [Blockly.Msg.senseBox_hum, "humidity"]]; this.appendDummyInput() .appendField(Blockly.Msg.senseBox_scd30); this.appendDummyInput() @@ -318,6 +318,14 @@ Blockly.Blocks['sensebox_scd30'] = { .appendField(new Blockly.FieldDropdown(dropdownOptions), "dropdown") this.setOutput(true, Types.NUMBER.typeName); this.setColour(getColour().sensebox); - this.setTooltip(Blockly.Msg.senseBox_bme_tip); + this.setTooltip(Blockly.Msg.senseBox_scd_tip); + }, + onchange: function (e) { + var dropdown = this.getFieldValue('dropdown'); + if (dropdown === 'temperature' || dropdown === 'humidity') { + this.setOutput(true, Types.DECIMAL.typeName); + } else if (dropdown === 'CO2') { + this.setOutput(true, Types.NUMBER.typeName); + } } }; diff --git a/src/components/Blockly/generator/generator.js b/src/components/Blockly/generator/generator.js index 7ff0fd4..5f63027 100644 --- a/src/components/Blockly/generator/generator.js +++ b/src/components/Blockly/generator/generator.js @@ -44,7 +44,7 @@ Blockly['Arduino'].addReservedWords( 'setup,loop,if,else,for,switch,case,while,' + 'do,break,continue,return,goto,define,include,' + 'HIGH,LOW,INPUT,OUTPUT,INPUT_PULLUP,true,false,' + - 'interger, constants,floating,point,void,bookean,char,' + + 'interger, constants,floating,point,void,boolean,char,' + 'unsigned,byte,int,word,long,float,double,string,String,array,' + 'static, volatile,const,sizeof,pinMode,digitalWrite,digitalRead,' + 'analogReference,analogRead,analogWrite,tone,noTone,shiftOut,shitIn,' + diff --git a/src/components/Blockly/generator/procedures.js b/src/components/Blockly/generator/procedures.js index a97b11b..963594f 100644 --- a/src/components/Blockly/generator/procedures.js +++ b/src/components/Blockly/generator/procedures.js @@ -28,3 +28,97 @@ Blockly.Arduino['arduino_functions'] = function (block) { //var loopcode = Blockly.Arduino.scrub_(block, loopBranch); No comment block return loopBranch; }; + +Blockly.Arduino['procedures_defreturn'] = function (block: Block | any) { + // Define a procedure with a return value. + const funcName = Blockly.Arduino.variableDB_.getName( + block.getFieldValue('NAME'), + Blockly.Procedures.NAME_TYPE + ); + const branch = Blockly.Arduino.statementToCode(block, 'STACK'); + const returnType = block.getFieldValue('RETURN TYPE') || 'void'; + + let returnValue = + Blockly.Arduino.valueToCode(block, 'RETURN', Blockly.Arduino.ORDER_NONE) || + ''; + if (returnValue) { + returnValue = Blockly.Arduino.INDENT + 'return ' + returnValue + ';\n'; + } + const args = []; + for (let i = 0; i < block.argumentVarModels_.length; i++) { + args[i] = + translateType(block.argumentVarModels_[i].type) + + ' ' + + block.argumentVarModels_[i].name; + } + let code = + translateType(returnType) + + ' ' + + funcName + + '(' + + args.join(', ') + + ') {\n' + + branch + + returnValue + + '}'; + code = Blockly.Arduino.scrub_(block, code); + // Add % so as not to collide with helper functions in definitions list. + Blockly.Arduino.functionNames_['%' + funcName] = code; + return null; +}; + +function translateType(type) { + switch (type) { + case 'Number': + return 'double'; + case 'String': + return 'String'; + case 'Boolean': + return 'boolean'; + case 'void': + return 'void'; + default: + throw new Error('Invalid Parameter Type'); + } +} + +Blockly.Arduino['procedures_defnoreturn'] = + Blockly.Arduino['procedures_defreturn']; + +Blockly.Arduino['procedures_callreturn'] = function (block) { + // Call a procedure with a return value. + const funcName = Blockly.Arduino.variableDB_.getName( + block.getFieldValue('NAME'), + Blockly.Procedures.NAME_TYPE + ); + const args = []; + for (let i = 0; i < block.arguments_.length; i++) { + args[i] = + Blockly.Arduino.valueToCode( + block, + 'ARG' + i, + Blockly.Arduino.ORDER_COMMA + ) || 'null'; + } + const code = funcName + '(' + args.join(', ') + ')'; + return [code, Blockly.Arduino.ORDER_ATOMIC]; +}; + +Blockly.Arduino['procedures_callnoreturn'] = function (block) { + // Call a procedure with no return value. + const funcName = Blockly.Arduino.variableDB_.getName( + block.getFieldValue('NAME'), + Blockly.Procedures.NAME_TYPE + ); + const args = []; + for (let i = 0; i < block.arguments_.length; i++) { + args[i] = + Blockly.Arduino.valueToCode( + block, + 'ARG' + i, + Blockly.Arduino.ORDER_COMMA + ) || 'null'; + } + + return funcName + '(' + args.join(', ') + ');\n'; +}; \ No newline at end of file diff --git a/src/components/Blockly/generator/sensebox-sensors.js b/src/components/Blockly/generator/sensebox-sensors.js index 6599e4a..106725e 100644 --- a/src/components/Blockly/generator/sensebox-sensors.js +++ b/src/components/Blockly/generator/sensebox-sensors.js @@ -263,32 +263,22 @@ Blockly.Arduino.sensebox_scd30 = function () { Blockly.Arduino.libraries_['scd30_library'] = '#include "SparkFun_SCD30_Arduino_Library.h"' Blockly.Arduino.libraries_['library_senseBoxMCU'] = '#include "SenseBoxMCU.h"'; Blockly.Arduino.definitions_['SCD30'] = 'SCD30 airSensor;'; - Blockly.Arduino.variables_['scd30_temp'] = 'float scd30_temp;'; - Blockly.Arduino.variables_['scd30_humi'] = 'float scd30_humi;'; - Blockly.Arduino.variables_['scd30_co2'] = 'float scd30_co2;'; Blockly.Arduino.setupCode_['init_scd30'] = ` Wire.begin(); if (airSensor.begin() == false) { - Serial.println("Air sensor not detected. Please check wiring. Freezing..."); while (1) ; }`; - Blockly.Arduino.loopCodeOnce_['scd30_getData'] = `if (airSensor.dataAvailable()) - { - scd30_co2 = airSensor.getCO2(); - scd30_temp = airSensor.getTemperature(); - scd30_humi = airSensor.getHumidity(); - }` var code = ''; switch (dropdown) { case 'temperature': - code = 'scd30_temp'; + code = 'aireSensor.getTemperature()'; break; case 'humidity': - code = 'scd30_humi'; + code = 'airSensor.getHumiditiy()'; break; case 'CO2': - code = 'scd30_co2'; + code = 'aireSensor.getCO2()'; break; default: code = '' diff --git a/src/components/Blockly/helpers/types.js b/src/components/Blockly/helpers/types.js index f7cacc7..407f31a 100644 --- a/src/components/Blockly/helpers/types.js +++ b/src/components/Blockly/helpers/types.js @@ -89,7 +89,7 @@ export const CHILD_BLOCK_MISSING = { const compatibleTypes = { Array: ['Array'], boolean: ['boolean'], - int: ['int'], + int: ['int', 'long', 'double', 'float'], char: ['char'], String: ['String'], void: ['void'], diff --git a/src/components/Blockly/msg/de.js b/src/components/Blockly/msg/de.js index bc722be..3012065 100644 --- a/src/components/Blockly/msg/de.js +++ b/src/components/Blockly/msg/de.js @@ -774,6 +774,8 @@ Blockly.Msg.senseBox_telegram_message = "Nachricht" Blockly.Msg.senseBox_telegram_send = "Sende Nachricht" //SCD30 CO2 Sensor Blockly.Msg.senseBox_scd30 = "CO2 Sensor (Sensirion SCD30)"; +Blockly.Msg.senseBox_scd_tip = "Gibt den Wert des CO2 Sensors"; +Blockly.Msg.senseBox_scd_co2 = "CO2 in ppm"; //WS2818 RGB LED Blockly.Msg.senseBox_ws2818_rgb_led = "senseBox WS2812 - RGB LED"; Blockly.Msg.senseBox_ws2818_rgb_led_position = "Position"; diff --git a/src/components/Blockly/msg/en.js b/src/components/Blockly/msg/en.js index 3b66e33..82cc519 100644 --- a/src/components/Blockly/msg/en.js +++ b/src/components/Blockly/msg/en.js @@ -757,6 +757,9 @@ Blockly.Msg.sensebox_soil_smt50 = "Soil Moisture and Temperature (SMT50)"; Blockly.Msg.sensebox_web_readHTML_filename = "File:"; //SCD30 CO2 Sensor Blockly.Msg.senseBox_scd30 = "CO2 Sensor (Sensirion SCD30)"; +Blockly.Msg.senseBox_scd_co2 = "CO2 in ppm"; +Blockly.Msg.senseBox_scd_tip = "Returns value of the CO2 Sensor"; + //WS2818 RGB LED Blockly.Msg.senseBox_ws2818_rgb_led = "senseBox WS2812 - RGB LED"; diff --git a/src/components/Blockly/toolbox/Toolbox.js b/src/components/Blockly/toolbox/Toolbox.js index 95d2322..286715d 100644 --- a/src/components/Blockly/toolbox/Toolbox.js +++ b/src/components/Blockly/toolbox/Toolbox.js @@ -379,6 +379,7 @@ class Toolbox extends React.Component { + diff --git a/src/components/Gallery/GalleryHome.js b/src/components/Gallery/GalleryHome.js index 6b55c1a..630681b 100644 --- a/src/components/Gallery/GalleryHome.js +++ b/src/components/Gallery/GalleryHome.js @@ -58,7 +58,6 @@ class GalleryHome extends Component { } componentDidMount() { - console.log(process.env.REACT_APP_BLOCKLY_API) fetch(process.env.REACT_APP_BLOCKLY_API + this.props.location.pathname) .then(res => res.json()) .then((data) => { diff --git a/src/components/Home.js b/src/components/Home.js index f54b129..683ad46 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -88,19 +88,12 @@ class Home extends Component { } render() { - // console.log(this.props.match.params.galleryId); - // console.log(gallery); - // console.log(gallery.filter(project => project.id == this.props.match.params.galleryId)); - if (this.state.projectToLoad) { - console.log(this.state.projectToLoad.xml) - } - console.log(this.props); return (
- + {this.state.codeOn ? - + : null} diff --git a/src/components/Tutorial/Builder/Builder.js b/src/components/Tutorial/Builder/Builder.js index cca4296..4c88177 100644 --- a/src/components/Tutorial/Builder/Builder.js +++ b/src/components/Tutorial/Builder/Builder.js @@ -51,7 +51,6 @@ class Builder extends Component { } submit = () => { - console.log(this.props.id) if (this.props.id === null) { var randomID = Date.now(); } else { @@ -115,7 +114,6 @@ class Builder extends Component { this.props.readJSON(result); this.setState({ snackbar: true, key: Date.now(), message: `${isFile ? 'Die übergebene JSON-Datei' : 'Der übergebene JSON-String'} wurde erfolgreich übernommen.`, type: 'success' }); } catch (err) { - console.log(err); this.props.progress(false); this.props.jsonString(''); this.setState({ open: true, string: false, title: 'Ungültiges JSON-Format', content: `${isFile ? 'Die übergebene Datei' : 'Der übergebene String'} enthält nicht valides JSON. Bitte überprüfe ${isFile ? 'die JSON-Datei' : 'den JSON-String'} und versuche es erneut.` }); diff --git a/src/components/Tutorial/HintTutorialExists.js b/src/components/Tutorial/HintTutorialExists.js index fa9b64f..4b1fa2d 100644 --- a/src/components/Tutorial/HintTutorialExists.js +++ b/src/components/Tutorial/HintTutorialExists.js @@ -27,10 +27,9 @@ const styles = (theme) => ({ class HintTutorialExists extends Component { - constructor(props){ + constructor(props) { var previousPageWasAnotherDomain = props.pageVisits === 0; var userDoNotWantToSeeNews = window.localStorage.getItem('news') ? true : false; - console.log(userDoNotWantToSeeNews); super(props); this.state = { open: userDoNotWantToSeeNews ? !userDoNotWantToSeeNews : previousPageWasAnotherDomain @@ -42,7 +41,7 @@ class HintTutorialExists extends Component { } onChange = (e) => { - if(e.target.checked){ + if (e.target.checked) { window.localStorage.setItem('news', e.target.checked); } else { @@ -66,8 +65,8 @@ class HintTutorialExists extends Component {
Es gibt ab jetzt Tutorials zu verschiedenen Themen. Schau mal hier vorbei. Date: Thu, 5 Nov 2020 14:38:38 +0100 Subject: [PATCH 2/3] small fixes --- src/components/Blockly/blocks/procedures.js | 59 ++++++++++--------- .../Blockly/generator/procedures.js | 12 ++-- src/components/Blockly/helpers/types.js | 3 +- src/components/Blockly/msg/de.js | 5 ++ src/components/Blockly/msg/en.js | 8 ++- src/components/Blockly/toolbox/Toolbox.js | 2 +- 6 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/components/Blockly/blocks/procedures.js b/src/components/Blockly/blocks/procedures.js index 237e4da..c334f8f 100644 --- a/src/components/Blockly/blocks/procedures.js +++ b/src/components/Blockly/blocks/procedures.js @@ -48,7 +48,7 @@ Blockly.Blocks['procedures_defnoreturn'] = { const nameField = new Blockly.FieldTextInput('', Blockly.Procedures.rename); nameField.setSpellcheck(false); this.appendDummyInput() - .appendField('Create block') + .appendField(Blockly.Msg.PROCEDURES_DEFNORETURN) .appendField(nameField, 'NAME') .appendField('', 'PARAMS'); this.setMutator(new Blockly.Mutator(['procedures_mutatorarg'])); @@ -127,7 +127,6 @@ Blockly.Blocks['procedures_defnoreturn'] = { paramString = Blockly.Msg['PROCEDURES_BEFORE_PARAMS'] + paramStringArgs.join(', '); } - console.log(paramString); // The params field is deterministic based on the mutation, // no need to fire a change event. Blockly.Events.disable(); @@ -454,24 +453,17 @@ Blockly.Blocks['procedures_defreturn'] = { */ init: function () { const returnTypeField = new Blockly.FieldDropdown( - [ - ['Number', 'Number'], - ['String', 'String'], - ['Boolean', 'Boolean'], - ['List Number', 'List Number'], - ['List String', 'List String'], - ['List Boolean', 'List Boolean'] - ], + [['NUMBER', 'int'], ['DECIMAL', 'float'], ['TEXT', 'String'], ['CHARACTER', 'char'], ['BOOLEAN', 'boolean']], this.updateReturnType.bind(this) ); const nameField = new Blockly.FieldTextInput('', Blockly.Procedures.rename); nameField.setSpellcheck(false); this.appendDummyInput() - .appendField('Create block') + .appendField(Blockly.Msg.PROCEDURES_DEFNORETURN) .appendField(nameField, 'NAME') .appendField('', 'PARAMS') - .appendField('that returns: ') + .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN_TYPE) .appendField(returnTypeField, 'RETURN TYPE'); this.appendValueInput('RETURN') @@ -590,16 +582,17 @@ Blockly.Blocks['procedures_mutatorcontainer'] = { } }; -let parameterCounter = 1; + Blockly.Blocks['procedures_mutatorarg'] = { + /** * Mutator block for procedure argument. * @this Blockly.Block */ init: function () { - const paramName = 'param' + parameterCounter.toString(); - + // let parameterCounter = 1; + const paramName = 'x';//'param' + parameterCounter.toString(); // This is for dialog box that get's opened // It has a flyout menu with the default variable // This will set the name of that default variable @@ -610,14 +603,7 @@ Blockly.Blocks['procedures_mutatorarg'] = { } const typeField = new Blockly.FieldDropdown( - [ - ['Number', 'Number'], - ['String', 'String'], - ['Boolean', 'Boolean'], - ['List Number', 'List Number'], - ['List String', 'List String'], - ['List Boolean', 'List Boolean'] - ], + [['NUMBER', 'int'], ['DECIMAL', 'float'], ['TEXT', 'String'], ['CHARACTER', 'char'], ['BOOLEAN', 'boolean']], this.validatorType_.bind(this) ); const nameField = new Blockly.FieldTextInput( @@ -651,7 +637,8 @@ Blockly.Blocks['procedures_mutatorarg'] = { // though the editor was never opened. this.createdVariables_ = []; nameField.onFinishEditing_(paramName); - parameterCounter += 1; + + // parameterCounter = 1; }, /** * Obtain a valid name for the procedure argument. Create a variable if @@ -725,6 +712,18 @@ Blockly.Blocks['procedures_mutatorarg'] = { this.createdVariables_.push(model); } } + + + + + // for (let i = 0; i < this.createdVariables_.length; i++) { + // const model = this.createdVariables_[i]; + // if (model.name !== varName || model.type !== varType) { + // // DELETE CRITERIA NEEDS TO CHANGE + + // } + // } + }, /** @@ -736,11 +735,11 @@ Blockly.Blocks['procedures_mutatorarg'] = { deleteIntermediateVars_: function (varName, varType) { varName = varName || this.getFieldValue('NAME'); varType = varType || this.getFieldValue('TYPE'); - const outerWs = Blockly.mainWorkspace; if (!outerWs) { return; } + for (let i = 0; i < this.createdVariables_.length; i++) { const model = this.createdVariables_[i]; if (model.name !== varName || model.type !== varType) { @@ -757,7 +756,10 @@ Blockly.Blocks['procedures_callnoreturn'] = { * @this Blockly.Block */ init: function () { - this.appendDummyInput('TOPROW').appendField(this.id, 'NAME'); + this.appendDummyInput('TOPROW') + .appendField(Blockly.Msg.PROCEDURES_CALL) + .appendField(this.id, 'NAME') + .appendField(Blockly.Msg.PROCEDURES_CALL_END); this.setPreviousStatement(true); this.setNextStatement(true); this.setColour(getColour().procedures); @@ -939,7 +941,7 @@ Blockly.Blocks['procedures_callnoreturn'] = { Blockly.Events.disable(); try { field.setValue(labelString); - this.getInput('ARG' + i).setCheck([this.argumentVarModels_[i].type]); + this.getInput('ARG' + i).setCheck(Types.getCompatibleTypes([this.argumentVarModels_[i].type])); } finally { Blockly.Events.enable(); } @@ -949,7 +951,7 @@ Blockly.Blocks['procedures_callnoreturn'] = { const input = this.appendValueInput('ARG' + i) .setAlign(Blockly.ALIGN_RIGHT) .appendField(field, 'ARGNAME' + i) - .setCheck([this.argumentVarModels_[i].type]); // TESTING CHECK TYPES GOES HERE + .setCheck(Types.getCompatibleTypes([this.argumentVarModels_[i].type])); // TESTING CHECK TYPES GOES HERE input.init(); } } @@ -1012,6 +1014,7 @@ Blockly.Blocks['procedures_callnoreturn'] = { for (let i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { if (childNode.nodeName.toLowerCase() === 'arg') { var variables = Blockly.mainWorkspace.getAllVariables(); + var varName = childNode.getAttribute('name'); for (let y = 0; variables.length; y++) { if (variables[y].name === varName) { diff --git a/src/components/Blockly/generator/procedures.js b/src/components/Blockly/generator/procedures.js index 963594f..a695a0a 100644 --- a/src/components/Blockly/generator/procedures.js +++ b/src/components/Blockly/generator/procedures.js @@ -68,15 +68,19 @@ Blockly.Arduino['procedures_defreturn'] = function (block: Block | any) { }; function translateType(type) { + console.log(type); switch (type) { - case 'Number': - return 'double'; + + case 'int': + return 'int'; case 'String': return 'String'; - case 'Boolean': - return 'boolean'; case 'void': return 'void'; + case 'boolean': + return 'boolean'; + case 'float': + return 'float' default: throw new Error('Invalid Parameter Type'); } diff --git a/src/components/Blockly/helpers/types.js b/src/components/Blockly/helpers/types.js index 407f31a..7a663d5 100644 --- a/src/components/Blockly/helpers/types.js +++ b/src/components/Blockly/helpers/types.js @@ -95,7 +95,8 @@ const compatibleTypes = { void: ['void'], long: ['int', 'long'], double: ['int', 'long', 'double'], - float: ['int', 'long', 'double', 'float'] + float: ['int', 'long', 'double', 'float'], + null: ['null'] } diff --git a/src/components/Blockly/msg/de.js b/src/components/Blockly/msg/de.js index 3012065..bba098d 100644 --- a/src/components/Blockly/msg/de.js +++ b/src/components/Blockly/msg/de.js @@ -272,6 +272,11 @@ Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL = "https://de.wikipedia.org/wiki/Pro Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP = "Rufe einen Funktionsblock ohne Rückgabewert auf."; Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL = "https://de.wikipedia.org/wiki/Prozedur_%28Programmierung%29"; Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP = "Rufe einen Funktionsblock mit Rückgabewert auf."; +Blockly.Msg.PROCEDURES_CALL = "Rufe"; +Blockly.Msg.PROCEDURES_CALL_END = "auf"; +Blockly.Msg.PROCEDURES_DEFNORETURN = "Erstelle Funktion"; +Blockly.Msg.PROCEDURES_BEFORE_PARAMS = "mit Eingabeparameter:"; +Blockly.Msg.PROCEDURES_DEFRETURN_RETURN_TYPE = "Rückgabetype"; Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS = "mit:"; Blockly.Msg.PROCEDURES_CREATE_DO = "Erzeuge \"Aufruf %1\""; Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT = "Beschreibe diese Funktion …"; diff --git a/src/components/Blockly/msg/en.js b/src/components/Blockly/msg/en.js index 82cc519..cc35bc9 100644 --- a/src/components/Blockly/msg/en.js +++ b/src/components/Blockly/msg/en.js @@ -267,20 +267,24 @@ Blockly.Msg.NEW_VARIABLE = "New variable..."; Blockly.Msg.NEW_VARIABLE_TITLE = "New variable name:"; Blockly.Msg.ORDINAL_NUMBER_SUFFIX = ""; Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS = "allow statements"; -Blockly.Msg.PROCEDURES_BEFORE_PARAMS = "with:"; +Blockly.Msg.PROCEDURES_BEFORE_PARAMS = "with inputs:"; Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP = "Run the user-defined function '%1'."; Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP = "Run the user-defined function '%1' and use its output."; -Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS = "with:"; +Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS = "with Inputs:"; +Blockly.Msg.PROCEDURES_CALL = "Call"; +Blockly.Msg.PROCEDURES_CALL_END = ''; Blockly.Msg.PROCEDURES_CREATE_DO = "Create '%1'"; Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT = "Describe this function..."; +Blockly.Msg.PROCEDURES_DEFNORETURN = "Create Function"; Blockly.Msg.PROCEDURES_DEFNORETURN_DO = ""; Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE = "do something"; Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE = "to"; Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP = "Creates a function with no output."; Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; +Blockly.Msg.PROCEDURES_DEFRETURN_RETURN_TYPE = "return Type"; Blockly.Msg.PROCEDURES_DEFRETURN_RETURN = "return"; Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP = "Creates a function with an output."; Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING = "Warning: This function has duplicate parameters."; diff --git a/src/components/Blockly/toolbox/Toolbox.js b/src/components/Blockly/toolbox/Toolbox.js index 286715d..f09b931 100644 --- a/src/components/Blockly/toolbox/Toolbox.js +++ b/src/components/Blockly/toolbox/Toolbox.js @@ -379,7 +379,7 @@ class Toolbox extends React.Component { - + From 70c8a1db603ba694e73a4fd53680ce3581681abd Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 5 Nov 2020 18:30:27 +0100 Subject: [PATCH 3/3] add feature to hide Workspace stats --- .../Blockly/generator/procedures.js | 2 +- src/components/Home.js | 8 +- src/components/Settings/RenderSelector.js | 2 +- src/components/Settings/Settings.js | 2 + src/components/Settings/StatsSelector.js | 44 +++++++ src/components/WorkspaceStats.js | 112 +++++++++--------- 6 files changed, 110 insertions(+), 60 deletions(-) create mode 100644 src/components/Settings/StatsSelector.js diff --git a/src/components/Blockly/generator/procedures.js b/src/components/Blockly/generator/procedures.js index a695a0a..7e07532 100644 --- a/src/components/Blockly/generator/procedures.js +++ b/src/components/Blockly/generator/procedures.js @@ -29,7 +29,7 @@ Blockly.Arduino['arduino_functions'] = function (block) { return loopBranch; }; -Blockly.Arduino['procedures_defreturn'] = function (block: Block | any) { +Blockly.Arduino['procedures_defreturn'] = function (block) { // Define a procedure with a return value. const funcName = Blockly.Arduino.variableDB_.getName( block.getFieldValue('NAME'), diff --git a/src/components/Home.js b/src/components/Home.js index 683ad46..85b2870 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -49,7 +49,8 @@ class Home extends Component { codeOn: false, gallery: [], share: [], - projectToLoad: undefined + projectToLoad: undefined, + stats: window.localStorage.getItem('stats'), } componentDidMount() { @@ -90,8 +91,11 @@ class Home extends Component { render() { return (
+ {this.state.stats ? +
+ : null + }
-
diff --git a/src/components/Settings/RenderSelector.js b/src/components/Settings/RenderSelector.js index 46ad84e..5cf7a9f 100644 --- a/src/components/Settings/RenderSelector.js +++ b/src/components/Settings/RenderSelector.js @@ -15,7 +15,7 @@ const useStyles = makeStyles((theme) => ({ }, })); -export default function LanguageSelector() { +export default function RenderSelector() { const classes = useStyles(); const [renderer, setRenderer] = React.useState(window.localStorage.getItem('renderer')); diff --git a/src/components/Settings/Settings.js b/src/components/Settings/Settings.js index 95b33dc..cbbedc9 100644 --- a/src/components/Settings/Settings.js +++ b/src/components/Settings/Settings.js @@ -5,6 +5,7 @@ import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; import LanguageSelector from './LanguageSelector'; import RenderSelector from './RenderSelector'; +import StatsSelector from './StatsSelector'; class Settings extends Component { @@ -14,6 +15,7 @@ class Settings extends Component { Einstellungen +