123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- "use strict";
- /**
- * Constants for application
- *
- * manifestList is the url that contains all possible manifests
- * pollTime is used to check for changes in the serial ports
- *
- * @type {{manifestList: string, pollTime: number}}
- */
- const CONSTANTS = {
- manifestList: "http://flasher.thingssdk.com/v1.1/manifest-list.json",
- pollTime: 1000
- };
- let isFlashing = false;
- let last_notification = "";
- /************************
- * Backend dependencies.
- * Note: Paths are relative to index.html not app.js
- ************************/
- const RomComm = require("rom-comm");
- const SerialScanner = require("../back-end/serial_scanner");
- const PortSelect = require("./js/port_select");
- const prepareBinaries = require("../back-end/prepare_binaries");
- const log = require("../back-end/logger");
- const serialScanner = new SerialScanner();
- /************************
- * UI Elements
- ************************/
- const flashButton = $("flash-button");
- const appStatus = $("status");
- const portsSelect = new PortSelect($("ports"));
- const manifestsSelect = $("manifests");
- const svg = $("Layer_1");
- const appWrapper = $("app");
- const logoWrapper = $("logo");
- const form = $("form");
- /************************
- * Utility Functions
- ************************/
- /**
- * Simple helper returns HTML element from an element's id
- *
- * @param id a string of the id of an HTML element
- * @returns {Element}
- */
- function $(id) { return document.getElementById(id); }
- /**
- * Processes the response from an HTTP fetch and returns the JSON promise.
- *
- * @param response from a fetch call
- * @returns {Promise}
- */
- function processJSON(response) {
- return response.json();
- }
- /************************
- * Handle UI
- ************************/
- flashButton.addEventListener("click", event => {
- isFlashing = true;
- disableInputs();
- prepareUIForFlashing(() => {
- fetch(manifestsSelect.value)
- .then(processJSON)
- .then(flashWithManifest);
- });
- });
- /************************
- * Manage serial port events
- ************************/
- serialScanner.on("ports", ports => {
- portsSelect.addAll(ports);
- });
- serialScanner.on("deviceAdded", port => {
- portsSelect.add(port);
- new Notification(`Added: ${port}!`);
- });
- serialScanner.on("deviceRemoved", port => {
- portsSelect.remove(port);
- new Notification(`Removed: ${port}!`);
- });
- serialScanner.on("error", err => {
- if(err.message === "No serial ports detected.") {
- if(portsSelect.children[0]) {
- portsSelect.remove(portsSelect.children[0].textContent);
- }
- }
- onError(err);
- });
- /**
- * Updates UI to say it's ready
- */
- function readyToFlash() {
- appStatus.textContent = "Ready";
- enableInputs();
- }
- /**
- * Enabled the serial port SELECT and flash BUTTON elements.
- */
- function enableInputs() {
- if(flashButton.disabled) {
- portsSelect.disabled = false;
- manifestsSelect.disabled = false;
- flashButton.disabled = false;
- }
- }
- function disableInputs() {
- if(!flashButton.disabled) {
- portsSelect.disabled = true;
- manifestsSelect.disabled = true;
- flashButton.disabled = true;
- }
- }
- /**
- * Generic catch all error. Shows notification at the moment.
- * @param error
- */
- function onError(error){
- if(last_notification !== error.message) {
- last_notification = error.message;
- new Notification(last_notification);
- }
- appStatus.textContent = error.message;
- }
- function generateManifestList(manifestsJSON) {
- manifestsJSON.options.forEach(option => {
- option.versions.forEach(version => {
- const optionElement = document.createElement("option");
- optionElement.textContent = `${option.name} - ${version.version}`;
- optionElement.value = version.manifest;
- manifestsSelect.appendChild(optionElement);
- manifestsSelect.disabled = false;
- });
- });
- }
- function getManifests() {
- appStatus.textContent = "Getting latest manifests.";
- // Break the cache to get the latest
- fetch(CONSTANTS.manifestList)
- .then(processJSON)
- .then(generateManifestList).catch(error => {
- setTimeout(getManifests, CONSTANTS.pollTime);
- });
- }
- function updateProgressUI(percent, display) {
- percent = Math.round(percent * 100);
- updateProgressBar(percent, svg);
- appStatus.textContent = `${display} - ${percent}%`;
- }
- function flashWithManifest(manifest) {
- appStatus.textContent = `Flashing ${portsSelect.value}`;
- prepareBinaries(manifest)
- .on("error", err => {
- isFlashing = false;
- onError(err);
- restoreUI();
- })
- .on("progress", progress => {
- //For the download/extract progress.
- updateProgressUI(progress.details.downloadedBytes / progress.details.downloadSize, progress.display);
- })
- .on("complete", flashSpec => {
- const device = RomComm.serial(portsSelect.value, {baudRate: 460800}, {
- onProgress: progress => updateProgressUI(progress.flashedBytes / progress.totalBytes, 'Flashing')
- });
- device.open((err) => {
- appStatus.textContent = `Flashing device connected to ${portsSelect.value}`;
- device.flash(flashSpec, (err) => {
- // TODO: This err doesn't come through
- if (err) {
- isFlashing = false;
- onError(err);
- restoreUI();
- }
- new Notification("Flash Finished!");
- isFlashing = false;
- restoreUI();
- });
- });
- });
- }
- function cloneSVGNode(node) {
- return node.cloneNode(true);
- }
- function updateClass(node) {
- node.setAttribute("class", "bg");
- return node;
- }
- function updateProgressBar(percent, svg){
- const line = svg.getElementsByClassName("st0")[0];
- const startDot = svg.getElementsByClassName("st1")[0];
- const finishDot = svg.getElementsByClassName("st2")[0];
- let backgroundElements = svg.getElementsByClassName("bg");
- if(backgroundElements.length === 0) {
- const g = svg.getElementsByTagName("g")[0];
- backgroundElements = [line, startDot, finishDot]
- .map(cloneSVGNode)
- .map(updateClass);
- backgroundElements.forEach(element => g.insertBefore(element, line));
- }
- const bgLine = backgroundElements[0];
- line.points.clear();
- if( percent < 1 ) {
- startDot.style.opacity = 0;
- } else {
- startDot.style.opacity = 1;
- }
- if( percent > 99 ) {
- finishDot.style.opacity = 1;
- } else {
- finishDot.style.opacity = 0;
- }
- for(let i = 0; i < percent * (bgLine.points.numberOfItems / 100); i ++) {
- if(i < bgLine.points.numberOfItems) {
- const point = bgLine.points.getItem(i);
- const newPoint = svg.createSVGPoint();
- newPoint.x = point.x;
- newPoint.y = point.y;
- line.points.appendItem(newPoint);
- }
- }
- }
- function prepareUIForFlashing(callback) {
- let percent = 100;
- appWrapper.classList.remove("finished");
- appWrapper.classList.add("flashing");
- let percentInterval = setInterval(() => {
- percent -= 1;
- updateProgressBar(percent, svg);
- if(percent === 0) {
- clearInterval(percentInterval);
- if(callback) callback();
- }
- }, 1);
- }
- function restoreUI(callback) {
- appWrapper.classList.remove("flashing");
- appWrapper.classList.add("finished");
- updateProgressBar(100, svg);
- if(callback) callback();
- }
- function inputStateManager() {
- if(!isFlashing) {
- if( manifestsSelect.children.length > 0 && portsSelect.children.length > 0 ) {
- readyToFlash();
- } else {
- disableInputs();
- }
- } else {
- disableInputs();
- }
- }
- /**
- * Get's manifest list for possibilities for flashing,
- * scans serial ports and sets up timer for checking for changes.
- */
- function start() {
- getManifests();
- serialScanner.scan();
- setInterval(() => {
- serialScanner.checkForChanges();
- inputStateManager();
- }, CONSTANTS.pollTime);
- }
- /**
- * Start Application
- */
- start();
|