diff --git a/package-lock.json b/package-lock.json index 32336909..0383913b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,59 +84,59 @@ } }, "@sentry/browser": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.25.0.tgz", - "integrity": "sha512-QDVUbUuTu58xCdId0eUO4YzpvrPdoUw1ryVy/Yep9Es/HD0fiSyO1Js0eQVkV/EdXtyo2pomc1Bpy7dbn2EJ2w==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.29.2.tgz", + "integrity": "sha512-uxZ7y7rp85tJll+RZtXRhXPbnFnOaxZqJEv05vJlXBtBNLQtlczV5iCtU9mZRLVHDtmZ5VVKUV8IKXntEqqDpQ==", "requires": { - "@sentry/core": "5.25.0", - "@sentry/types": "5.25.0", - "@sentry/utils": "5.25.0", + "@sentry/core": "5.29.2", + "@sentry/types": "5.29.2", + "@sentry/utils": "5.29.2", "tslib": "^1.9.3" } }, "@sentry/core": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.25.0.tgz", - "integrity": "sha512-hY6Zmo7t/RV+oZuvXHP6nyAj/QnZr2jW0e7EbL5YKMV8q0vlnjcE0LgqFXme726OJemoLk67z+sQOJic/Ztehg==", - "requires": { - "@sentry/hub": "5.25.0", - "@sentry/minimal": "5.25.0", - "@sentry/types": "5.25.0", - "@sentry/utils": "5.25.0", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.2.tgz", + "integrity": "sha512-7WYkoxB5IdlNEbwOwqSU64erUKH4laavPsM0/yQ+jojM76ErxlgEF0u//p5WaLPRzh3iDSt6BH+9TL45oNZeZw==", + "requires": { + "@sentry/hub": "5.29.2", + "@sentry/minimal": "5.29.2", + "@sentry/types": "5.29.2", + "@sentry/utils": "5.29.2", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.25.0.tgz", - "integrity": "sha512-kOlOiJV8wMX50lYpzMlOXBoH7MNG0Ho4RTusdZnXZBaASq5/ljngDJkLr6uylNjceZQP21wzipCQajsJMYB7EQ==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.2.tgz", + "integrity": "sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ==", "requires": { - "@sentry/types": "5.25.0", - "@sentry/utils": "5.25.0", + "@sentry/types": "5.29.2", + "@sentry/utils": "5.29.2", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.25.0.tgz", - "integrity": "sha512-9JFKuW7U+1vPO86k3+XRtJyooiVZsVOsFFO4GulBzepi3a0ckNyPgyjUY1saLH+cEHx18hu8fGgajvI8ANUF2g==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.2.tgz", + "integrity": "sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw==", "requires": { - "@sentry/hub": "5.25.0", - "@sentry/types": "5.25.0", + "@sentry/hub": "5.29.2", + "@sentry/types": "5.29.2", "tslib": "^1.9.3" } }, "@sentry/types": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.25.0.tgz", - "integrity": "sha512-8M4PREbcar+15wrtEqcwfcU33SS+2wBSIOd/NrJPXJPTYxi49VypCN1mZBDyWkaK+I+AuQwI3XlRPCfsId3D1A==" + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.2.tgz", + "integrity": "sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA==" }, "@sentry/utils": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.25.0.tgz", - "integrity": "sha512-Hz5spdIkMSRH5NR1YFOp5qbsY5Ud2lKhEQWlqxcVThMG5YNUc10aYv5ijL19v0YkrC2rqPjCRm7GrVtzOc7bXQ==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.2.tgz", + "integrity": "sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ==", "requires": { - "@sentry/types": "5.25.0", + "@sentry/types": "5.29.2", "tslib": "^1.9.3" } }, diff --git a/package.json b/package.json index 81f9b998..be21a3ae 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ }, "homepage": "https://github.com/parallaxinc/solo#readme", "dependencies": { - "@sentry/browser": "^5.25.0", + "@sentry/browser": "^5.29.2", "ace-builds": "^1.4.8", "blockly": "^2.20190722.1", "bootbox": "^5.4.0", diff --git a/src/modules/blockly/generators/propc.js b/src/modules/blockly/generators/propc.js index c2958e2a..22deaff3 100644 --- a/src/modules/blockly/generators/propc.js +++ b/src/modules/blockly/generators/propc.js @@ -399,6 +399,12 @@ Blockly.propc.addReservedWords( 'ws2812_set,ws2812_start,ws2812_stop,ws2812_wheel,ws2812_wheel_dim,' + 'ws2812b_open,ws2812b_start'); +/** + * SD File reserved words + */ +Blockly.propc.addReservedWords( + 'fp,fclose,fopen,fread,fseek,ftell,fwrite,sd_init,sd_mount,'); + /** * Order of operation ENUMs. * diff --git a/src/modules/blockly/generators/propc/communicate.js b/src/modules/blockly/generators/propc/communicate.js index 1f4ec3a9..d8f5c090 100644 --- a/src/modules/blockly/generators/propc/communicate.js +++ b/src/modules/blockly/generators/propc/communicate.js @@ -836,23 +836,23 @@ Blockly.propc.console_print_multiple = function() { orIt = '0'; } + // Get the resulting code from Blockly core + const result = Blockly.propc.valueToCode( + this, 'PRINT' + i, Blockly.propc.ORDER_NONE) || orIt; + if (!this.getFieldValue('TYPE' + i).includes('float point divide by')) { - varList += ', ' + (Blockly.propc.valueToCode( - this, - 'PRINT' + i, - Blockly.propc.ORDER_NONE).replace(/%/g, '%%') || orIt); + varList += ', ' + result; } else { - varList += ', ((float) ' + (Blockly.propc.valueToCode( - this, - 'PRINT' + i, - Blockly.propc.ORDER_NONE) || orIt) + - ') / ' + this.getFieldValue('DIV' + i) + '.0'; + varList += ', ((float) ' + result + ') / ' + + this.getFieldValue('DIV' + i) + '.0'; } i++; } + if (this.getFieldValue('ck_nl') === 'TRUE') { code += '\\r'; } + code += '"' + varList + ');\n'; // TODO: Replace .getAllBlocks() with getAllBlocksByType() @@ -1527,46 +1527,60 @@ Blockly.Blocks.serial_receive_text = { onchange: Blockly.Blocks['serial_send_text'].onchange, }; + /** * Serial Receive Text code generator * @return {string} + * @description Generate C source code to scan data from a serial port input. */ Blockly.propc.serial_receive_text = function() { let p = ''; + + /* Is a serial pin defined? */ if (this.ser_pins.length > 0) { - p = this.ser_pins[0].replace(',', '_').replace(/None/g, 'N'); + p = this.ser_pins[0] + .replace(',', '_') // Replace commas with underscores + .replace(/None/g, 'N'); // Replace 'None' with 'N' in all occurrences. } + + /* Get serial pins in use */ if (this.getInput('SERPIN')) { p = this.getFieldValue('SER_PIN') .replace(',', '_') .replace(/None/g, 'N'); } - const allBlocks = Blockly.getMainWorkspace().getAllBlocks().toString(); - if (allBlocks.indexOf('Serial initialize') === -1) { + + if (! Blockly.getMainWorkspace() + .getBlocksByType('Serial initialize', false)) { return '// ERROR: Serial is not initialized!\n'; } else { - const data = Blockly.propc.variableDB_.getName( - this.getFieldValue('VALUE'), - Blockly.VARIABLE_CATEGORY_NAME); + const data = Blockly.propc.variableDB_ + .getName(this.getFieldValue('VALUE'), Blockly.VARIABLE_CATEGORY_NAME); - const type = this.getFieldValue('TYPE'); + switch (this.getFieldValue('TYPE')) { + case 'BYTE': + return data + ' = fdserial_rxChar(fdser' + p + ');\n'; - if (type === 'BYTE') { - return data + ' = fdserial_rxChar(fdser' + p + ');\n'; - } else if (type === 'INT') { - return 'dscan(fdser' + p + ', "%d", &' + data + ');\n'; - } else if (type === 'BIN') { - return 'dscan(fdser' + p + ', "%b", &' + data + ');\n'; - } else if (type === 'HEX') { - return 'dscan(fdser' + p + ', "%x", &' + data + ');\n'; - } else { - Blockly.propc.vartype_[data] = 'char *'; + case 'INT': + return 'dscan(fdser' + p + ', "%d", &' + data + ');\n'; + + case 'BIN': + return 'dscan(fdser' + p + ', "%b", &' + data + ');\n'; + + case 'HEX': + return 'dscan(fdser' + p + ', "%x", &' + data + ');\n'; - return 'dscan(fdser' + p + ', "%s", ' + data + ');\n'; + case 'TEXT': + default: + Blockly.propc.vartype_[data] = 'char *'; } + + // This will return a string up to the first whitespace character. + return 'dscan(fdser' + p + ', "%s", ' + data + ');\n'; } }; + /** * Serial Status block definition * @type {{ diff --git a/src/modules/blockly/generators/propc/gpio.js b/src/modules/blockly/generators/propc/gpio.js index 69300839..269122fb 100644 --- a/src/modules/blockly/generators/propc/gpio.js +++ b/src/modules/blockly/generators/propc/gpio.js @@ -2249,417 +2249,6 @@ Blockly.propc.wav_stop = function() { return 'wav_stop();\n'; }; -// ----------------- SD Card file blocks -------------------------- - -/** - * SD Card Initialization - * @type {{ - * init: Blockly.Blocks.sd_init.init, - * helpUrl: string - * }} - */ -Blockly.Blocks.sd_init = { - helpUrl: Blockly.MSG_SD_HELPURL, - init: function() { - const profile = getDefaultProfile(); - this.setTooltip(Blockly.MSG_SD_INIT_TOOLTIP); - this.setColour(colorPalette.getColor('output')); - this.appendDummyInput() - .appendField('SD initialize DO') - .appendField(new Blockly.FieldDropdown( - profile.digital), 'DO') - .appendField('CLK') - .appendField(new Blockly.FieldDropdown( - profile.digital), 'CLK') - .appendField('DI') - .appendField(new Blockly.FieldDropdown( - profile.digital), 'DI') - .appendField('CS') - .appendField(new Blockly.FieldDropdown( - profile.digital), 'CS'), - this.setPreviousStatement(true, 'Block'); - this.setNextStatement(true, null); - }, -}; - -/** - * - * @return {string} - */ -Blockly.propc.sd_init = function() { - if (!this.disabled) { - Blockly.propc.setups_['sd_card'] = 'sd_mount(' + - this.getFieldValue('DO') + ', ' + - this.getFieldValue('CLK') + ', ' + - this.getFieldValue('DI') + ', ' + - this.getFieldValue('CS') + ');'; - } - - return ''; -}; - -/** - * - * @type {{ - * init: Blockly.Blocks.sd_open.init, - * helpUrl: string, - * onchange: Blockly.Blocks.sd_open.onchange - * }} - */ -Blockly.Blocks.sd_open = { - helpUrl: Blockly.MSG_SD_HELPURL, - init: function() { - this.setTooltip(Blockly.MSG_SD_OPEN_TOOLTIP); - this.setColour(colorPalette.getColor('output')); - this.appendDummyInput('MODE') - .appendField('SD file open') - .appendField(new Blockly.FieldTextInput( - 'filename.txt', - function(filename) { - filename = filename.replace(/[^A-Z0-9a-z_.]/g, '').toLowerCase(); - const filenamePart = filename.split('.'); - if (filenamePart[0].length > 8) { - filenamePart[0].length = 8; - } - if (!filenamePart[1]) { - filenamePart[1] = 'TXT'; - } else if (filenamePart[1].length > 3) { - filenamePart[1].length = 3; - } - return filenamePart[0] + '.' + filenamePart[1]; - }), 'FILENAME') - .appendField(new Blockly.FieldDropdown([ - ['as read-only', 'r'], - ['as read-write', 'w'], - ]), 'MODE'); - this.setInputsInline(false); - this.setPreviousStatement(true, 'Block'); - this.setNextStatement(true, null); - }, - onchange: function() { - const project = getProjectInitialState(); - if (project.boardType.name !== 'activity-board' && - project.boardType.name !== 'heb-wx') { - const allBlocks = Blockly.getMainWorkspace().getAllBlocks().toString(); - if (allBlocks.indexOf('SD initialize') === -1) { - this.setWarningText('WARNING: You must use a SD' + - ' initialize\nblock at the beginning of your program!'); - } else { - this.setWarningText(null); - } - } - }, -}; - -/** - * SD Card Open C code generator - * @return {string} - */ -Blockly.propc.sd_open = function() { - const profile = getDefaultProfile(); - const project = getProjectInitialState(); - const fp = this.getFieldValue('FILENAME'); - const mode = this.getFieldValue('MODE'); - let head = ''; - let i = 0; - let initFound = false; - - const allBlocks = Blockly.getMainWorkspace().getAllBlocks(); - for (let x = 0; x < allBlocks.length; x++) { - if (allBlocks[x].type === 'sd_open') { - i++; - if (allBlocks[x] === this && i === 1) { - head = 'FILE* '; - } - } - if (allBlocks[x].type === 'sd_init') { - initFound = true; - } - } - - if (!this.disabled && !initFound && profile.sd_card) { - Blockly.propc.setups_['sd_card'] = 'sd_mount(' + profile.sd_card + ');'; - } - - let code = head + 'fp = fopen("' + fp + '","' + mode + '");'; - if (project.boardType.name !== 'activity-board' && - project.boardType.name !== 'heb-wx' && - allBlocks.toString().indexOf('SD initialize') === -1) { - code = '// WARNING: You must use a SD initialize block at the' + - ' beginning of your program!'; - } - return code; -}; - -/** - * - * @type {{ - * init: Blockly.Blocks.sd_read.init, - * mutationToDom: (function(): HTMLElement), - * helpUrl: string, - * setSdMode: Blockly.Blocks.sd_read.setSdMode, - * onchange: Blockly.Blocks.sd_read.onchange, - * domToMutation: Blockly.Blocks.sd_read.domToMutation - * }} - */ -Blockly.Blocks.sd_read = { - helpUrl: Blockly.MSG_SD_HELPURL, - init: function() { - this.setTooltip(Blockly.MSG_SD_READ_TOOLTIP); - this.setColour(colorPalette.getColor('output')); - this.setSdMode('fwrite'); - this.setInputsInline(true); - this.setPreviousStatement(true, 'Block'); - this.setNextStatement(true, null); - }, - mutationToDom: function() { - const container = document.createElement('mutation'); - container.setAttribute('mode', this.getFieldValue('MODE')); - return container; - }, - domToMutation: function(container) { - const mode = container.getAttribute('mode'); - if (mode) { - this.setFieldValue(mode, 'MODE'); - } - this.setSdMode(mode); - }, - setSdMode: function(mode) { - let connectedBlock = null; - - if (this.getInput('SIZE')) { - const valueConnection = this.getInput('SIZE').connection; - if (valueConnection) { - connectedBlock = valueConnection.targetBlock(); - } - this.removeInput('SIZE'); - } - if (this.getInput('VALUE')) { - this.removeInput('VALUE'); - } - if (mode === 'fwrite') { - this.appendValueInput('SIZE') - .setCheck(null) - .appendField('SD file') - .appendField(new Blockly.FieldDropdown([ - ['write', 'fwrite'], - ['read', 'fread'], - ['close', 'fclose'], - ], function(mode) { - // eslint-disable-next-line no-invalid-this - this.getSourceBlock().setSdMode(mode); - }), 'MODE'); - this.appendValueInput('VALUE') - .setCheck('String') - .appendField('bytes of'); - } else if (mode === 'fread') { - this.appendValueInput('SIZE') - .setCheck('Number') - .appendField('SD file') - .appendField(new Blockly.FieldDropdown([ - ['read', 'fread'], - ['write', 'fwrite'], - ['close', 'fclose'], - ], function(mode) { - // eslint-disable-next-line no-invalid-this - this.getSourceBlock().setSdMode(mode); - }), 'MODE'); - this.appendDummyInput('VALUE') - .appendField('bytes store in') - .appendField(new Blockly.FieldVariable( - Blockly.LANG_VARIABLES_SET_ITEM), 'VAR'); - } else { - this.appendDummyInput('SIZE') - .appendField('SD file') - .appendField(new Blockly.FieldDropdown([ - ['close', 'fclose'], - ['read', 'fread'], - ['write', 'fwrite'], - ], function(mode) { - // eslint-disable-next-line no-invalid-this - this.getSourceBlock().setSdMode(mode); - }), 'MODE'); - } - if (connectedBlock) { - connectedBlock.outputConnection.connect(this.getInput('SIZE').connection); - } - }, - onchange: function(event) { - const project = getProjectInitialState(); - if (event.type === Blockly.Events.BLOCK_DELETE || - event.type === Blockly.Events.BLOCK_CREATE) { - let warnTxt = null; - const allBlocks = Blockly.getMainWorkspace().getAllBlocks().toString(); - if (allBlocks.indexOf('SD file open') === -1) { - warnTxt = 'WARNING: You must use a SD file open block\nbefore' + - ' reading, writing, or closing an SD file!'; - } else if (allBlocks.indexOf('SD initialize') === -1 && - project.boardType.name !== 'heb-wx' && - project.boardType.name !== 'activity-board') { - warnTxt = 'WARNING: You must use a SD initialize\nblock at the' + - ' beginning of your program!'; - } - this.setWarningText(warnTxt); - } - }, -}; - -/** - * SD Card Read C code generator - * @return {string} - */ -Blockly.propc.sd_read = function() { - const project = getProjectInitialState(); - const profile = getDefaultProfile(); - const size = Blockly.propc.valueToCode( - this, 'SIZE', Blockly.propc.ORDER_NONE) || '1'; - const mode = this.getFieldValue('MODE'); - let value = ''; - let code = ''; - - if (mode === 'fread') { - value = Blockly.propc.variableDB_.getName( - this.getFieldValue('VAR'), - Blockly.VARIABLE_CATEGORY_NAME); - value = '&' + value; - Blockly.propc.vartype_[value] = 'char *'; - } else if (mode === 'fwrite') { - value = Blockly.propc.valueToCode( - this, 'VALUE', Blockly.propc.ORDER_NONE) || ''; - } - - if (mode === 'fclose') { - code = mode + '(fp);'; - } else { - code = mode + '(' + value + ', 1, ' + size + ', fp);'; - // code = mode + '(&' + value + ', 1, ' + size + ', fp);'; - } - - const allBlocks = Blockly.getMainWorkspace().getAllBlocks().toString(); - if (allBlocks.indexOf('SD file open') === -1) { - code = '// WARNING: You must use a SD file open block before reading,' + - ' writing, or closing an SD file!'; - } else if (allBlocks.indexOf('SD initialize') === -1 && - project.boardType.name !== 'heb-wx' && - project.boardType.name !== 'activity-board') { - code = '// WARNING: You must use a SD initialize block at the' + - ' beginning of your program!'; - } - - let initFound = false; - for (let x = 0; x < allBlocks.length; x++) { - if (allBlocks[x].type === 'sd_init') { - initFound = true; - } - } - - if (!this.disabled && !initFound && profile.sd_card) { - Blockly.propc.setups_['sd_card'] = 'sd_mount(' + profile.sd_card + ');'; - } - - return code; -}; - -/** - * - * @type {{ - * init: Blockly.Blocks.sd_file_pointer.init, - * mutationToDom: *, - * helpUrl: string, - * setSdMode: Blockly.Blocks.sd_file_pointer.setSdMode, - * onchange: *, - * domToMutation: * - * }} - */ -Blockly.Blocks.sd_file_pointer = { - helpUrl: Blockly.MSG_SD_HELPURL, - init: function() { - this.setTooltip(Blockly.MSG_SD_FILE_POINTER_TOOLTIP); - this.setColour(colorPalette.getColor('output')); - this.setSdMode('set'); - this.setInputsInline(false); - this.setPreviousStatement(true, 'Block'); - this.setNextStatement(true, null); - }, - mutationToDom: Blockly.Blocks['sd_read'].mutationToDom, - domToMutation: Blockly.Blocks['sd_read'].domToMutation, - setSdMode: function(mode) { - if (this.getInput('FP')) { - this.removeInput('FP'); - } - if (mode === 'set') { - this.appendValueInput('FP') - .setCheck('Number') - .appendField('SD file') - .appendField(new Blockly.FieldDropdown([ - ['set', 'set'], - ['get', 'get'], - ], function(blockMode) { - // eslint-disable-next-line no-invalid-this - this.getSourceBlock().setSdMode(blockMode); - }), 'MODE') - .appendField('pointer = '); - this.setOutput(false); - this.setPreviousStatement(true, 'Block'); - this.setNextStatement(true, null); - } else { - this.appendDummyInput('FP'); - this.getInput('FP') - .appendField('SD file') - .appendField(new Blockly.FieldDropdown([ - ['get', 'get'], - ['set', 'set'], - ], function(blockMode) { - // eslint-disable-next-line no-invalid-this - this.getSourceBlock().setSdMode(blockMode); - }), 'MODE') - .appendField('pointer'); - this.setPreviousStatement(false, 'Block'); - this.setNextStatement(false, null); - this.setOutput(true, 'Number'); - } - }, - onchange: Blockly.Blocks['sd_read'].onchange, -}; - -/** - * SD Card File Pointer - * @return {(string|number)[]|string} - */ -Blockly.propc.sd_file_pointer = function() { - const profile = getDefaultProfile(); - const project = getProjectInitialState(); - // TODO: Refactor getAllBlocks to getAllBlocksByType - const allBlocks = Blockly.getMainWorkspace().getAllBlocks().toString(); - let code = null; - let initFound = false; - for (let x = 0; x < allBlocks.length; x++) { - if (allBlocks[x].type === 'sd_init') { - initFound = true; - } - } - - if (!this.disabled && !initFound && profile.sd_card) { - Blockly.propc.setups_['sd_card'] = 'sd_mount(' + profile.sd_card + ');'; - } - - if (allBlocks.indexOf('SD file open') === -1) { - code = '// WARNING: You must use a SD file open block before' + - ' using the file pointer!'; - } else if (allBlocks.indexOf('SD initialize') === -1 && - project.boardType.name !== 'heb-wx' && - project.boardType.name !== 'activity-board') { - code = '// WARNING: You must use a SD initialize block at the' + - ' beginning of your program!'; - } else if (this.getFieldValue('MODE') === 'set') { - const fp = Blockly.propc.valueToCode( - this, 'FP', Blockly.propc.ORDER_NONE) || '0'; - code = 'fp = ' + fp + ';'; - } else { - code = ['fp', Blockly.propc.ORDER_ATOMIC]; - } - return code; -}; // ----------------- Robot (drive) blocks ------------------------------------ diff --git a/src/modules/blockly/generators/propc/sd_card.js b/src/modules/blockly/generators/propc/sd_card.js new file mode 100644 index 00000000..65880933 --- /dev/null +++ b/src/modules/blockly/generators/propc/sd_card.js @@ -0,0 +1,484 @@ +/* + * TERMS OF USE: MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +import Blockly from 'blockly/core'; +import {getDefaultProfile, getProjectInitialState} from '../../../project'; +import {colorPalette} from '../propc'; + +const SdInitMissingMessage = + '/** WARNING: You must use a SD initialize block at the' + + ' beginning of your program! **/\n'; + +const SdOpenMissingMessage = + '/** WARNING: You must use a SD file open block before reading,' + + ' writing, or closing an SD file! **/\n'; + +/** + * SD Card Initialization + * @type {{ + * init: Blockly.Blocks.sd_init.init, + * helpUrl: string + * }} + */ +Blockly.Blocks.sd_init = { + helpUrl: Blockly.MSG_SD_HELPURL, + init: function() { + const profile = getDefaultProfile(); + this.setTooltip(Blockly.MSG_SD_INIT_TOOLTIP); + this.setColour(colorPalette.getColor('output')); + this.appendDummyInput() + .appendField('SD initialize DO') + .appendField(new Blockly.FieldDropdown( + profile.digital), 'DO') + .appendField('CLK') + .appendField(new Blockly.FieldDropdown( + profile.digital), 'CLK') + .appendField('DI') + .appendField(new Blockly.FieldDropdown( + profile.digital), 'DI') + .appendField('CS') + .appendField(new Blockly.FieldDropdown( + profile.digital), 'CS'), + this.setPreviousStatement(true, 'Block'); + this.setNextStatement(true, null); + }, +}; + +/** + * Generate source code for the sd_init block. + * This creates a global file pointer. + * + * @return {string} + */ +Blockly.propc.sd_init = function() { + // Global variable for SD file processing + this.myType = 'fp'; + + if (!this.disabled) { + Blockly.propc.setups_['sd_card'] = 'sd_mount(' + + this.getFieldValue('DO') + ', ' + + this.getFieldValue('CLK') + ', ' + + this.getFieldValue('DI') + ', ' + + this.getFieldValue('CS') + ');'; + + // Declare the global variable + Blockly.propc.global_vars_[ + this.myType + 'global'] = 'FILE *' + this.myType + ';'; + } + + return ''; +}; + +/** + * + * @type {{ + * init: Blockly.Blocks.sd_open.init, + * helpUrl: string, + * onchange: Blockly.Blocks.sd_open.onchange + * }} + */ +Blockly.Blocks.sd_open = { + helpUrl: Blockly.MSG_SD_HELPURL, + init: function() { + this.setTooltip(Blockly.MSG_SD_OPEN_TOOLTIP); + this.setColour(colorPalette.getColor('output')); + this.appendDummyInput('MODE') + .appendField('SD file open') + .appendField(new Blockly.FieldTextInput( + 'filename.txt', + function(filename) { + filename = filename.replace(/[^A-Z0-9a-z_.]/g, '').toLowerCase(); + const filenamePart = filename.split('.'); + if (filenamePart[0].length > 8) { + filenamePart[0].length = 8; + } + if (!filenamePart[1]) { + filenamePart[1] = 'TXT'; + } else if (filenamePart[1].length > 3) { + filenamePart[1].length = 3; + } + return filenamePart[0] + '.' + filenamePart[1]; + }), 'FILENAME') + .appendField(new Blockly.FieldDropdown([ + ['as read-only', 'r'], + ['as read-write', 'w'], + ]), 'MODE'); + this.setInputsInline(false); + this.setPreviousStatement(true, 'Block'); + this.setNextStatement(true, null); + }, + + /** + * Check for an active sd_init block in the project. The block must exist + * and not be disabled. + */ + onchange: function() { + const project = getProjectInitialState(); + if (project.boardType.name !== 'activity-board' && + project.boardType.name !== 'heb-wx') { + const block = Blockly.getMainWorkspace().getBlocksByType( + 'sd_init', false); + if (block.length === 0 || ! block[0].isEnabled()) { + this.setWarningText('WARNING: You must use a SD' + + ' initialize\nblock at the beginning of your program!'); + } else { + this.setWarningText(null); + } + } + }, +}; + +/** + * SD Card Open C code generator + * @return {string} + */ +Blockly.propc.sd_open = function() { + const initSdBlock = Blockly.getMainWorkspace().getBlocksByType( + 'sd_init', false); + if (initSdBlock.length === 0 || !initSdBlock[0].isEnabled()) { + const project = getProjectInitialState(); + if (project.boardType.name !== 'activity-board' && + project.boardType.name !== 'heb-wx') { + return SdOpenMissingMessage; + } else { + // Quietly mount the sd card filesystem + setupSdCard(); + } + } + + const filename = this.getFieldValue('FILENAME'); + const mode = this.getFieldValue('MODE'); + return `fp = fopen("${filename}","${mode}");\n`; +}; + +/** + * + * @type {{ + * init: Blockly.Blocks.sd_read.init, + * mutationToDom: (function(): HTMLElement), + * helpUrl: string, + * setSdMode: Blockly.Blocks.sd_read.setSdMode, + * onchange: Blockly.Blocks.sd_read.onchange, + * domToMutation: Blockly.Blocks.sd_read.domToMutation + * }} + */ +Blockly.Blocks.sd_read = { + helpUrl: Blockly.MSG_SD_HELPURL, + init: function() { + this.setTooltip(Blockly.MSG_SD_READ_TOOLTIP); + this.setColour(colorPalette.getColor('output')); + this.setSdMode('fwrite'); + this.setInputsInline(true); + this.setPreviousStatement(true, 'Block'); + this.setNextStatement(true, null); + }, + mutationToDom: function() { + const container = document.createElement('mutation'); + container.setAttribute('mode', this.getFieldValue('MODE')); + return container; + }, + domToMutation: function(container) { + const mode = container.getAttribute('mode'); + if (mode) { + this.setFieldValue(mode, 'MODE'); + } + this.setSdMode(mode); + }, + setSdMode: function(mode) { + let connectedBlock = null; + + if (this.getInput('SIZE')) { + const valueConnection = this.getInput('SIZE').connection; + if (valueConnection) { + connectedBlock = valueConnection.targetBlock(); + } + this.removeInput('SIZE'); + } + if (this.getInput('VALUE')) { + this.removeInput('VALUE'); + } + if (mode === 'fwrite') { + // Ensure that the field only receives numeric data + this.appendValueInput('SIZE') + .setCheck('Number') + .appendField('SD file') + .appendField(new Blockly.FieldDropdown([ + ['write', 'fwrite'], + ['read', 'fread'], + ['close', 'fclose'], + ], function(mode) { + // eslint-disable-next-line no-invalid-this + this.getSourceBlock().setSdMode(mode); + }), 'MODE'); + this.appendValueInput('VALUE') + .setCheck('String') + .appendField('bytes of'); + } else if (mode === 'fread') { + this.appendValueInput('SIZE') + .setCheck('Number') + .appendField('SD file') + .appendField(new Blockly.FieldDropdown([ + ['read', 'fread'], + ['write', 'fwrite'], + ['close', 'fclose'], + ], function(mode) { + // eslint-disable-next-line no-invalid-this + this.getSourceBlock().setSdMode(mode); + }), 'MODE'); + this.appendDummyInput('VALUE') + .appendField('bytes store in') + .appendField(new Blockly.FieldVariable( + Blockly.LANG_VARIABLES_SET_ITEM), 'VAR'); + } else { + this.appendDummyInput('SIZE') + .appendField('SD file') + .appendField(new Blockly.FieldDropdown([ + ['close', 'fclose'], + ['read', 'fread'], + ['write', 'fwrite'], + ], function(mode) { + // eslint-disable-next-line no-invalid-this + this.getSourceBlock().setSdMode(mode); + }), 'MODE'); + } + if (connectedBlock) { + connectedBlock.outputConnection.connect(this.getInput('SIZE').connection); + } + }, + onchange: function(event) { + const project = getProjectInitialState(); + if (event.type === Blockly.Events.BLOCK_DELETE || + event.type === Blockly.Events.BLOCK_CREATE) { + let warnTxt = null; + const allBlocks = Blockly.getMainWorkspace().getAllBlocks().toString(); + if (allBlocks.indexOf('SD file open') === -1) { + warnTxt = 'WARNING: You must use a SD file open block\nbefore' + + ' reading, writing, or closing an SD file!'; + } else if (allBlocks.indexOf('SD initialize') === -1 && + project.boardType.name !== 'heb-wx' && + project.boardType.name !== 'activity-board') { + warnTxt = 'WARNING: You must use a SD initialize\nblock at the' + + ' beginning of your program!'; + } + this.setWarningText(warnTxt); + } + }, +}; + +/** + * SD Card Read/Write/Close C code generator + * + * Generate code to read from an sd card, write to an sd card or to close a + * connection to the sd card reader. + * + * @return {string} + */ +Blockly.propc.sd_read = function() { + // Identify the block action (fread, fwrite, or fclose) + const mode = this.getFieldValue('MODE'); + + // Handle close stright away + if (mode === 'fclose') { + return `if(fp) ${mode}(fp);\n`; + } + + // Verify the required SD-Open block is in the project + const block = Blockly.getMainWorkspace().getBlocksByType( + 'sd_open', false); + + if ( block.length === 0 || (!block[0].isEnabled())) { + return SdOpenMissingMessage; + } + + /** + * Verify that for boards that do not have a built-in card reader, there is + * an sd_init block in the project + */ + const project = getProjectInitialState(); + let initFound = false; + + const initSdBlock = Blockly.getMainWorkspace().getBlocksByType( + 'sd_init', false); + if (initSdBlock.length > 0 && initSdBlock[0].isEnabled()) { + initFound = true; + } + + if (project.boardType.name !== 'heb-wx' && + project.boardType.name !== 'activity-board' && + ! initFound) { + return SdInitMissingMessage; + } + + // Silently mount the embedded sd card device + if (!this.disabled && !initFound) { + setupSdCard(); + } + + // Retrieve the number of bytes to read/write. Default to one byte + const size = Blockly.propc.valueToCode( + this, 'SIZE', Blockly.propc.ORDER_NONE) || '1'; + + // Retrieve the data buffer or variable + let value = ''; + if (mode === 'fread') { + value = Blockly.propc.variableDB_.getName( + this.getFieldValue('VAR'), + Blockly.VARIABLE_CATEGORY_NAME); + + value = '&' + value; + Blockly.propc.vartype_[value] = 'char *'; + } else if (mode === 'fwrite') { + value = Blockly.propc.valueToCode( + this, 'VALUE', Blockly.propc.ORDER_NONE) || ''; + } + + return `${mode}(${value}, 1, ${size}, fp);\n`; +}; + +/** + * + * @type {{ + * init: Blockly.Blocks.sd_file_pointer.init, + * mutationToDom: *, + * helpUrl: string, + * setSdMode: Blockly.Blocks.sd_file_pointer.setSdMode, + * onchange: *, + * domToMutation: * + * }} + */ +Blockly.Blocks.sd_file_pointer = { + helpUrl: Blockly.MSG_SD_HELPURL, + init: function() { + this.setTooltip(Blockly.MSG_SD_FILE_POINTER_TOOLTIP); + this.setColour(colorPalette.getColor('output')); + this.setSdMode('set'); + this.setInputsInline(false); + this.setPreviousStatement(true, 'Block'); + this.setNextStatement(true, null); + }, + mutationToDom: Blockly.Blocks['sd_read'].mutationToDom, + domToMutation: Blockly.Blocks['sd_read'].domToMutation, + setSdMode: function(mode) { + if (this.getInput('FP')) { + this.removeInput('FP'); + } + if (mode === 'set') { + this.appendValueInput('FP') + .setCheck('Number') + .appendField('SD file') + .appendField(new Blockly.FieldDropdown([ + ['set', 'set'], + ['get', 'get'], + ], function(blockMode) { + // eslint-disable-next-line no-invalid-this + this.getSourceBlock().setSdMode(blockMode); + }), 'MODE') + .appendField('pointer = '); + this.setOutput(false); + this.setPreviousStatement(true, 'Block'); + this.setNextStatement(true, null); + } else { + // mode == get + this.appendDummyInput('FP'); + this.getInput('FP') + .appendField('SD file') + .appendField(new Blockly.FieldDropdown([ + ['get', 'get'], + ['set', 'set'], + ], function(blockMode) { + // eslint-disable-next-line no-invalid-this + this.getSourceBlock().setSdMode(blockMode); + }), 'MODE') + .appendField('pointer'); + + this.setPreviousStatement(false); + this.setNextStatement(false, null); + this.setOutput(true, 'Number'); + } + }, + onchange: Blockly.Blocks['sd_read'].onchange, +}; + +/** + * SD Card File Pointer + * Retrieves or sets the file pointer of an open file. + * + * set - Uses the fseek function to position the file pointer to 'n' bytes + * from the beginning of the file. + * + * get - Returns an integer value indicating the current position of the + * file pointer in the file stream. + * + * @return {(string|number)[]|string} + * + */ +Blockly.propc.sd_file_pointer = function() { + const project = getProjectInitialState(); + // TODO: Refactor getAllBlocks to getAllBlocksByType + const allBlocks = Blockly.getMainWorkspace().getAllBlocks().toString(); + let code = null; + let initFound = false; + for (let x = 0; x < allBlocks.length; x++) { + if (allBlocks[x].type === 'sd_init') { + initFound = true; + } + } + + // Quietly install setup code + if (!this.disabled && !initFound) { + setupSdCard(); + } + + if (allBlocks.indexOf('SD file open') === -1) { + return '// WARNING: You must use a SD file open block before' + + ' using the file pointer!\n'; + } + + if (allBlocks.indexOf('SD initialize') === -1 && + project.boardType.name !== 'heb-wx' && + project.boardType.name !== 'activity-board') { + return SdInitMissingMessage; + } + + if (this.getFieldValue('MODE') === 'set') { + const fp = Blockly.propc.valueToCode( + this, 'FP', Blockly.propc.ORDER_NONE) || '0'; + code = `fseek(fp, ${fp}, SEEK_SET);\n`; + } else { + // Get pointer + code = ['ftell(fp)', Blockly.propc.ORDER_ATOMIC]; + } + + return code; +}; + + +/** + * Mount SD Card + */ +function setupSdCard() { + const profile = getDefaultProfile(); + if (profile.sd_card) { + Blockly.propc.setups_['sd_card'] = 'sd_mount(' + profile.sd_card + ');'; + Blockly.propc.global_vars_['fpglobal'] = 'FILE *fp;'; + } +} diff --git a/src/modules/constants.js b/src/modules/constants.js index daf92b42..676c42a5 100644 --- a/src/modules/constants.js +++ b/src/modules/constants.js @@ -44,7 +44,7 @@ export const EnableSentry = true; * {b#} is the beta release number. * {rc#} is the release candidate number. */ -export const APP_VERSION = '1.5.6'; +export const APP_VERSION = '1.5.7'; /** * Constant string that represents the base, empty project header diff --git a/src/modules/editor.js b/src/modules/editor.js index 8fd3cc58..2d817602 100644 --- a/src/modules/editor.js +++ b/src/modules/editor.js @@ -37,6 +37,7 @@ import './blockly/generators/propc/oled'; import './blockly/generators/propc/heb'; import './blockly/generators/propc/procedures'; import './blockly/generators/propc/s3'; +import './blockly/generators/propc/sd_card'; import './blockly/generators/propc/sensors'; import './blockly/generators/propc/variables'; diff --git a/webpack/dev.config.js b/webpack/dev.config.js index 2e597326..80779199 100644 --- a/webpack/dev.config.js +++ b/webpack/dev.config.js @@ -51,7 +51,8 @@ module.exports = merge(baseConfig, { poll: 1000, }, plugins: [ - new CopyPlugin([ + new CopyPlugin({ + patterns: [ { from: './index.html', to: path.resolve(__dirname, targetPath) @@ -59,27 +60,15 @@ module.exports = merge(baseConfig, { { from: './blocklyc.html', to: path.resolve(__dirname, targetPath) - } - ]), - - // Copy over media resources from the Blockly package - new CopyPlugin([ + }, { from: path.resolve(__dirname, '../node_modules/blockly/media'), to: path.resolve(__dirname, `${targetPath}/media`) - } - ]), - - // Copy over media resources from Solo images tree - new CopyPlugin([ + }, { from: './src/images', to: path.resolve(__dirname, `${targetPath}/images`) - } - ]), - - // Copy over style sheets - new CopyPlugin([ + }, { from: './src/site.css', to: path.resolve(__dirname, targetPath) @@ -96,7 +85,7 @@ module.exports = merge(baseConfig, { from: './src/style-editor.css', to: path.resolve(__dirname, targetPath) } - ]), - ], - } -); + ] + } + )] +}); diff --git a/webpack/prod.config.js b/webpack/prod.config.js index ea659b4d..c73d462d 100644 --- a/webpack/prod.config.js +++ b/webpack/prod.config.js @@ -28,81 +28,68 @@ const baseConfig = require('./base.config'); // const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = merge(baseConfig, { - // Use env. here: - // console.log('NODE_ENV: ', env.NODE_ENV); // 'local' - // console.log('Production: ', env.production); // true + // Use env. here: + // console.log('NODE_ENV: ', env.NODE_ENV); // 'local' + // console.log('Production: ', env.production); // true - mode: 'production', - devtool: 'source-map', - output: { - path: path.resolve(__dirname, '../dist'), - filename: '[name].bundle.[chunkhash].js', + mode: 'production', + devtool: 'source-map', + output: { + path: path.resolve(__dirname, '../dist'), + filename: '[name].bundle.[chunkhash].js', // chunkFilename: '[id].bundle.js', // pathinfo: true, - sourceMapFilename: '[name].bundle.[chunkhash].js.map', - }, + sourceMapFilename: '[name].bundle.[chunkhash].js.map', + }, // optimization: { // splitChunks: { // chunks: 'all', // }, // }, - module: { - rules: [ - { - test: /\.css$/, - use: [ - 'style-loader', - 'css-loader' - ] - } + module: { + rules: [ + { + test: /\.css$/, + use: [ + 'style-loader', + 'css-loader' ] - }, - plugins: [ - new CopyPlugin([ - { - from: './index.html', - to: path.resolve(__dirname, '../dist') - }, - { - from: './blocklyc.html', - to: path.resolve(__dirname, '../dist') - } - ]), - - // Copy over media resources from the Blockly package - new CopyPlugin([ - { - from: path.resolve(__dirname, '../node_modules/blockly/media'), - to: path.resolve(__dirname, '../dist/media') - } - ]), - - // Copy over media resources from Solo images tree - new CopyPlugin([ - { - from: './src/images', - to: path.resolve(__dirname, '../dist/images') - } - ]), - - // Copy over style sheets - new CopyPlugin([ - { - from: './src/site.css', - to: path.resolve(__dirname, '../dist') - }, - { - from: './src/style.css', - to: path.resolve(__dirname, '../dist') - }, - { - from: './src/style-clientdownload.css', - to: path.resolve(__dirname, '../dist') - }, - { - from: './src/style-editor.css', - to: path.resolve(__dirname, '../dist') - }, - ]), - ], - }); + } + ]}, + plugins: [ + new CopyPlugin({ + patterns: [ + { + from: './index.html', + to: path.resolve(__dirname, '../dist') + }, + { + from: './blocklyc.html', + to: path.resolve(__dirname, '../dist') + }, + { + // Copy over media resources from the Blockly package + from: path.resolve(__dirname, '../node_modules/blockly/media'), + to: path.resolve(__dirname, '../dist/media') + }, + { + // Copy over style sheets + from: './src/site.css', + to: path.resolve(__dirname, '../dist') + }, + { + from: './src/style.css', + to: path.resolve(__dirname, '../dist') + }, + { + from: './src/style-clientdownload.css', + to: path.resolve(__dirname, '../dist') + }, + { + from: './src/style-editor.css', + to: path.resolve(__dirname, '../dist') + }, + ] + }) + ] +});