Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proof of concept for downloading a file from microfs #216

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -443,4 +447,4 @@
"defaultMessage": "Zoom out",
"description": "Text label for zoom out button"
}
}
}
8 changes: 7 additions & 1 deletion src/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@
"value": "Download a project hex file"
}
],
"download-microfs-files": [
{
"type": 0,
"value": "Download MicroFS files"
}
],
"download-python": [
{
"type": 0,
Expand Down Expand Up @@ -877,4 +883,4 @@
"value": "Zoom out"
}
]
}
}
8 changes: 8 additions & 0 deletions src/project/DownloadFlashButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ const DownloadFlashButton = ({ size }: DownloadFlashButtonProps) => {
>
<FormattedMessage id="download-python" />
</MenuItem>
<MenuItem
target="_blank"
rel="noopener"
icon={<RiDownload2Line />}
onClick={actions.downloadMicrofsFiles}
>
<FormattedMessage id="download-microfs-files" />
</MenuItem>
</MenuList>
</Portal>
</ButtonGroup>
Expand Down
88 changes: 88 additions & 0 deletions src/project/project-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -374,6 +375,93 @@ export class ProjectActions {
}
};

downloadMicrofsFiles = async () => {
this.logging.event({
type: "download-microfs-files",
});
if ( this.device.status != "CONNECTED" )
{
// TODO: make my own error (or is there an appropriate one?)
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);

// 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');
}
}
};
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
'\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
];

// there's probably a more correct way to send one line at a time
// asynchronously
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.
*/
Expand Down