From 425b1c10b5f86c4c0603842d65d81cfd1f3c2620 Mon Sep 17 00:00:00 2001 From: Paul Grayson Date: Sat, 31 Jul 2021 14:33:30 -0700 Subject: [PATCH 1/3] proof of concept for downloading a file "data.txt" from the microfs --- lang/en.json | 6 ++- src/messages/en.json | 8 +++- src/project/DownloadFlashButton.tsx | 8 ++++ src/project/project-actions.tsx | 68 +++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/lang/en.json b/lang/en.json index 6c389384e..7c56e1110 100644 --- a/lang/en.json +++ b/lang/en.json @@ -175,6 +175,10 @@ "defaultMessage": "Download a project hex file", "description": "Hover text over download button" }, + "download-microfs-files": { + "defaultMessage": "Download MicroFS files", + "description": "Download button menu option for downloading on-board MicroFS files" + }, "download-python": { "defaultMessage": "Download Python script", "description": "Download button menu option for downloading Python script" @@ -443,4 +447,4 @@ "defaultMessage": "Zoom out", "description": "Text label for zoom out button" } -} \ No newline at end of file +} diff --git a/src/messages/en.json b/src/messages/en.json index c0a8eecea..3b6f7a205 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -355,6 +355,12 @@ "value": "Download a project hex file" } ], + "download-microfs-files": [ + { + "type": 0, + "value": "Download MicroFS files" + } + ], "download-python": [ { "type": 0, @@ -877,4 +883,4 @@ "value": "Zoom out" } ] -} \ No newline at end of file +} diff --git a/src/project/DownloadFlashButton.tsx b/src/project/DownloadFlashButton.tsx index 33e27766b..917ff3aff 100644 --- a/src/project/DownloadFlashButton.tsx +++ b/src/project/DownloadFlashButton.tsx @@ -93,6 +93,14 @@ const DownloadFlashButton = ({ size }: DownloadFlashButtonProps) => { > + } + onClick={actions.downloadMicrofsFiles} + > + + diff --git a/src/project/project-actions.tsx b/src/project/project-actions.tsx index 0d0907b02..866c1103a 100644 --- a/src/project/project-actions.tsx +++ b/src/project/project-actions.tsx @@ -39,6 +39,7 @@ import { isPythonFile, validateNewFilename, } from "./project-utils"; +import { EVENT_SERIAL_DATA, EVENT_SERIAL_RESET } from "../device/device"; /** * Distinguishes the different ways to trigger the load action. @@ -374,6 +375,73 @@ export class ProjectActions { } }; + downloadMicrofsFiles = async () => { + this.logging.event({ + type: "download-microfs-files", + }); + if ( this.device.status != "CONNECTED" ) + { + return this.webusbNotSupportedError(); + } + let output = "" + let foundOK = false + const serialListener = (data: string) => { + output = output + data; + if(!foundOK) + { + const okindex = output.indexOf("OK"); + if(okindex != -1) + { + output = output.substring(okindex+2); + foundOK = true; + } + } + if(foundOK) + { + const endindex = output.indexOf("END\r"); + if(endindex != -1) + { + output = output.substring(endindex, -1) + this.device.removeListener(EVENT_SERIAL_DATA, serialListener); + const lines = output.split("\r\n").map(l => l.replace(/([0-9a-z]{2})/g, x => unescape("%"+x))); + const blob = new Blob(lines, { + type: "application/octet-stream", + }); + saveAs(blob, 'data.txt'); + } + } +// output = output + JSON.parse(data) + }; + this.device.on(EVENT_SERIAL_DATA, serialListener); + const script = [ + '\x02', // Ctrl+B to end raw mode if required + '\x03', // Ctrl+C three times to break + '\x03', + '\x03', + '\x01', // Ctrl+A to enter raw mode + 'f = open("data.txt", "rb")\r\n', + 'r = f.read\r\n', + 'result = True\r\n', + 'while result:\r\n', + ' result = r(32)\r\n', + ' if result:\r\n', + ' print("".join("%02x" % i for i in result)+"\\r\\n")\r\n', + 'print("END\\r\\n")\r\n', + 'f.close()\r\n', + '\x04', // Ctrl+D to run script + '\x02', // Ctrl+B to exit raw mode + ]; + let i = 0; + let p = null; + const f = () => { + if (i >= script.length) return; + p = this.device.serialWrite(script[i]); + i = i + 1; + p.then(f); + } + f(); + }; + /** * Create a file, prompting the user for the name. */ From a83a39236193368147e2f9f99a3bbd54856829ce Mon Sep 17 00:00:00 2001 From: Paul Grayson Date: Sat, 31 Jul 2021 15:01:12 -0700 Subject: [PATCH 2/3] simpler url encoding, commenting --- src/project/project-actions.tsx | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/project/project-actions.tsx b/src/project/project-actions.tsx index 866c1103a..6447ce18b 100644 --- a/src/project/project-actions.tsx +++ b/src/project/project-actions.tsx @@ -403,16 +403,32 @@ export class ProjectActions { { output = output.substring(endindex, -1) this.device.removeListener(EVENT_SERIAL_DATA, serialListener); - const lines = output.split("\r\n").map(l => l.replace(/([0-9a-z]{2})/g, x => unescape("%"+x))); + + // We got a complete file. + // Decode the hex and send as a data URI. + const lines = output.split("\r\n").map(l => unescape(l)) const blob = new Blob(lines, { type: "application/octet-stream", }); saveAs(blob, 'data.txt'); } } -// output = output + JSON.parse(data) }; this.device.on(EVENT_SERIAL_DATA, serialListener); + + // This script is similar to the one used by microfs.py: It enters + // raw mode and reads the file 32 bytes at a time. Unlike + // microfs.py, which uses repr(), here we convert each byte to + // urlencoded hex. + // + // We probably need to figure out how to enter raw mode more + // reliably (microfs.py has some delays that I did not implement, + // and I have no error handling at all here) and disable Xterm + // output while doing the download. + // + // Note that there's an apparent bug with sending multiple lines + // at a time: + // https://github.com/microbit-foundation/python-editor-next/issues/215 const script = [ '\x02', // Ctrl+B to end raw mode if required '\x03', // Ctrl+C three times to break @@ -425,12 +441,15 @@ export class ProjectActions { 'while result:\r\n', ' result = r(32)\r\n', ' if result:\r\n', - ' print("".join("%02x" % i for i in result)+"\\r\\n")\r\n', + ' print("".join("%%%02x" % i for i in result)+"\\r\\n")\r\n', 'print("END\\r\\n")\r\n', 'f.close()\r\n', '\x04', // Ctrl+D to run script '\x02', // Ctrl+B to exit raw mode ]; + + // there's probably a more correct way to send one line at a time + // asynchronously let i = 0; let p = null; const f = () => { From b503f2e61f2238cee8641eb5f02290e142f51d3f Mon Sep 17 00:00:00 2001 From: Paul Grayson Date: Sat, 31 Jul 2021 15:02:20 -0700 Subject: [PATCH 3/3] commented error message --- src/project/project-actions.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/project/project-actions.tsx b/src/project/project-actions.tsx index 6447ce18b..06010859b 100644 --- a/src/project/project-actions.tsx +++ b/src/project/project-actions.tsx @@ -381,6 +381,7 @@ export class ProjectActions { }); if ( this.device.status != "CONNECTED" ) { + // TODO: make my own error (or is there an appropriate one?) return this.webusbNotSupportedError(); } let output = ""