Files
AI-on-the-edge-device/sd-card/html/ota_page.html
CaCO3 798f1423c3 Ignore cache on GIT hash change (new commit or release) (#1787)
* Add hash to all html, css, and js URLs

* Update build.yaml

* Update build.yaml

* .

* .

Co-authored-by: CaCO3 <caco@ruinelli.ch>
2023-01-08 19:18:21 +01:00

294 lines
12 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>OTA Update</title>
<meta charset="utf-8">
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
input[type=number] {
width: 138px;
padding: 10px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
.button {
padding: 10px 20px;
width: 211px;
font-size: 16px;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
</head>
<body style="font-family: arial; padding: 0px 10px;">
<h2>OTA Update</h2>
<p>Check the <a href="https://github.com/jomjol/AI-on-the-edge-device/releases" target=_blank>Release Page</a> to see if there is an update available. <br>
Then pick the <i><span style="font-family:monospace">AI-on-the-edge-device__update__*.zip</span></i> file!</p>
<p>Alternatively you can use a file in the following format:</p>
<ul>
<li><span style="font-family:monospace">*.zip</span></li>
<li><span style="font-family:monospace">*.bin</span></li>
<li><span style="font-family:monospace">*.tfl/tflite</span></li>
</ul>
<p>Make sure the file extention is lower case.</p>
<p><br><b>Do not reload the page or switch to another page while the update is in progress!</b><br></p>
<form id="upload_form" enctype="multipart/form-data" method="post">
<input type="file" accept=".bin,.zip,.tfl,.tflite" name="file_selector" id="file_selector" onchange="validate_file()"><br><br>
<button class="button" style="width:300px" id="start_OTA_button" type="button" onclick="start_OTA()" disabled>Upload and install</button>
<br><br>
<progress id="progressBar" value="0" max="100" style="width:600px;"></progress>
<p id="loaded_n_total"></p>
<h3><span id="status">Status: idle</span></h3>
</form>
<script language="JavaScript">
var domainname = getDomainname();
var action_runtime = 0;
/* Max size of an individual file. Make sure this
* value is same as that set in server_file.c */
var MAX_FILE_SIZE = 8000*1024;
var MAX_FILE_SIZE_STR = "8MB";
function validate_file() {
document.getElementById("start_OTA_button").disabled = true;
var fileInput = document.getElementById("file_selector").files;
var filepath = document.getElementById("file_selector").value;
console.log("filepath: " + filepath);
filename = filepath.split(/[\\\/]/).pop();
console.log("filename: " + filename);
/* Various checks on filename length and file size */
if (fileInput.length == 0) {
firework.launch('No file selected!', 'danger', 30000);
return;
} else if (filename.length == 0) {
firework.launch('File path on server is not set!', 'danger', 30000);
return;
} else if (filename.length > 100) {
firework.launch('Filename is to long! Max 100 characters.', 'danger', 30000);
return;
} else if (filename.indexOf(' ') >= 0) {
firework.launch('Filename can not have spaces!', 'danger', 30000);
return;
} else if (filename[filename.length-1] == '/') {
firework.launch('File name not specified after path!', 'danger', 30000);
return;
} else if (fileInput[0].size > MAX_FILE_SIZE) {
firework.launch("File size must be less than " + MAX_FILE_SIZE_STR + "!", 'danger', 30000);
return;
}
/* Check if the fillename matches our expected pattern
* - AI-on-the-edge-device__update__*.zip
* - firmware__*.bin
* - *.ftl
* - *.tflite */
if ( /(^AI-on-the-edge-device__update__)[a-z0-9()_\-.]*(\.zip$)/.test(filename) || // OK
( /(^AI-on-the-edge-device__firmware)[a-z0-9()_\-.]*(\.bin$)/.test(filename)) ||
( /[a-z0-9()_\-.]*(\.tfl$)/.test(filename)) ||
( /[a-z0-9()_\-.]*(\.tflite$)/.test(filename))) {
firework.launch('Great, the filename matches our expectations. You can now press "Upload and update".', 'success', 5000);
}
/* Following filenames are acceptiod but not prefered:
* - *.bin
* - *.zip */
else if (filename.endsWith(".zip") || filename.endsWith(".bin")) { // Warning but still accepted
firework.launch('The filename does not match the suggested file name pattern, but is nevertheless accepted. You can now press "Upload and install', 'warning', 10000);
}
/* Any other file name format is not accepted */
else { // invalid
firework.launch('The filename does not match our expectations!', 'danger', 30000);
return;
}
document.getElementById("start_OTA_button").disabled = false;
}
function start_OTA() {
document.getElementById("start_OTA_button").disabled = true;
var file_name = document.getElementById("file_selector").value;
document.getElementById("file_selector").disabled = true;
file_name = file_name.split(/[\\\/]/).pop();
document.getElementById("status").innerText = "Status: File selected";
prepareOnServer();
}
function doRebootAfterUpdate() {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "/reboot", true);
xhttp.send();
}
function prepareOnServer() {
document.getElementById("status").innerText = "Status: Preparing device...";
var xhttp = new XMLHttpRequest();
var file_name = document.getElementById("file_selector").value;
filePath = file_name.split(/[\\\/]/).pop();
/* first delete the old firmware AND empty the /firmware directory*/
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
/* keine Reaktion, damit sich das Dokument nicht ändert */
upload();
} else if (xhttp.status == 0) {
firework.launch('Server closed the connection abruptly!', 'danger', 30000);
} else {
firework.launch('An error occured: ' + xhttp.responseText, 'danger', 30000);
}
}
};
var _toDo = domainname + "/ota?task=emptyfirmwaredir";
xhttp.open("GET", _toDo, true);
xhttp.send();
}
function extract() {
document.getElementById("status").innerText = "Status: Processing on device...";
var xhttp = new XMLHttpRequest();
/* first delete the old firmware */
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
document.cookie = "page=overview.html?v=$COMMIT_HASH" + "; path=/"; // Make sure after the reboot we go to the overview page
if (xhttp.responseText.startsWith("reboot")) { // Reboot required
console.log("Upload completed, the device will now restart and install the update!");
document.getElementById("status").innerText = "Status: Installing...";
firework.launch('Upload completed, the device will now restart and install the update', 'success', 5000);
/* Tell it to reboot */
doRebootAfterUpdate();
action_runtime = 0;
updateTimer = setInterval(function() {
action_runtime += 1;
console.log("Waiting: " + action_runtime + "s");
_("progressBar").value = Math.round(action_runtime);
if (action_runtime > 10) { // After 10 seconds, start to check if we are up again
/* Check if the device is up again and forward to index page if so */
fetch('reboot_page.html?v=$COMMIT_HASH&' + Math.random(), {mode: 'no-cors'}).then(
r=>{parent.location.href=('index.html?v=$COMMIT_HASH');}
)
}
if (action_runtime > 100) { // We reached 300 seconds but device is not ready yet
firework.launch("The device seems not do be up again, or maybe we missed it. Try to reload this page or reset the device!", 'danger', 30000);
clearInterval(updateTimer);
}
}, 3000);
}
else // No reboot required
{
document.getElementById("status").innerText = "Status: Update completed";
firework.launch('Update completed!', 'success', 5000);
document.getElementById("file_selector").disabled = false;
}
} else if (xhttp.status == 0) {
firework.launch('Server closed the connection abruptly!', 'danger', 30000);
} else {
firework.launch('An error occured: ' + xhttp.responseText, 'danger', 30000);
}
}
};
var file_name = document.getElementById("file_selector").value;
filePath = file_name.split(/[\\\/]/).pop();
var _toDo = domainname + "/ota?task=update&file=" + filePath;
xhttp.open("GET", _toDo, true);
xhttp.send();
}
function _(el) {
return document.getElementById(el);
}
function upload() {
document.getElementById("status").innerText = "Status: Uploading...";
var upload_path = "/upload/firmware/" + filePath;
var file = _("file_selector").files[0];
var formdata = new FormData();
formdata.append("file_selector", file);
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", progressHandler, false);
ajax.addEventListener("load", completeHandler, false);
ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", upload_path);
ajax.send(file);
}
function progressHandler(event) {
_("loaded_n_total").innerHTML = "Uploaded " + (event.loaded / 1024 / 1024).toFixed(2) +
" MBytes of " + (event.total / 1024/ 1024).toFixed(2) + " MBytes.";
var percent = (event.loaded / event.total) * 100;
_("progressBar").value = Math.round(percent);
_("status").innerHTML = "Status: " + Math.round(percent) + "% uploaded... please wait";
}
function completeHandler(event) {
_("status").innerHTML = "Status: " + event.target.responseText;
_("progressBar").value = 0; //will clear progress bar after successful upload
_("loaded_n_total").innerHTML = "";
extract();
}
function errorHandler(event) {
_("status").innerHTML = "Status: Upload Failed";
firework.launch('Upload failed!', 'danger', 30000);
document.getElementById("file_selector").disabled = false;
}
function abortHandler(event) {
_("status").innerHTML = "Status: Upload Aborted";
firework.launch('Upload aborted!', 'danger', 30000);
document.getElementById("file_selector").disabled = false;
}
</script>
</body>
</html>