Message ID | 1515408521-31480-3-git-send-email-stefan@herbrechtsmeier.net |
---|---|
State | Changes Requested |
Headers | show |
Series | [1/3] progress_ipc: Add non-blocking receive support | expand |
Hi Stefan, On 08/01/2018 11:48, stefan@herbrechtsmeier.net wrote: > From: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> > > The new web page uses WebSocket for the asynchronous communication with the > backend, allows a visualization of different image sources and automatically > reloads the web page after a connection lost. > First at all, many thanks to provide a very responsive website, that looks very professional and add features that are currently missing in the simple website we currently have. It was at the beginning just an example for website, it was never changed). I agree to store this in a separate directory, and users can choose which website they want to have on target. I have some questions for my understanding: using npm is straightforward, but I get errors because I had a old version of nodejs. nodejs is not required on target, but for development we should write somewhere which are the minimal requirements. My next issue is with deployment. "npm install" creates a big bunch of packages that are not needed. As far as I can see in code (and from my tests), it should be enough for the website to include : node_modules/bootstrap node_modules/dropzone node_modules/jquery Is there an easy way to deploy the website ? After this will be merged here, recipes should be added for Yocto. > Signed-off-by: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> > --- > www2/.gitignore | 2 + > www2/LICENSE | 17 +++++++ > www2/README.md | 31 +++++++++++++ > www2/gulpfile.js | 99 ++++++++++++++++++++++++++++++++++++++++ > www2/index.html | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > www2/logo.png | Bin 0 -> 24403 bytes > www2/package.json | 38 ++++++++++++++++ > www2/recovery.css | 36 +++++++++++++++ > www2/recovery.js | 110 ++++++++++++++++++++++++++++++++++++++++++++ > 9 files changed, 466 insertions(+) > create mode 100644 www2/.gitignore > create mode 100644 www2/LICENSE > create mode 100644 www2/README.md > create mode 100755 www2/gulpfile.js > create mode 100755 www2/index.html > create mode 100644 www2/logo.png > create mode 100755 www2/package.json > create mode 100755 www2/recovery.css > create mode 100755 www2/recovery.js > > diff --git a/www2/.gitignore b/www2/.gitignore > new file mode 100644 > index 0000000..1eae0cf > --- /dev/null > +++ b/www2/.gitignore > @@ -0,0 +1,2 @@ > +dist/ > +node_modules/ > diff --git a/www2/LICENSE b/www2/LICENSE > new file mode 100644 > index 0000000..fb950dc > --- /dev/null > +++ b/www2/LICENSE > @@ -0,0 +1,17 @@ > +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 NONINFRINGEMENT. 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. For a FOSS project, it should be easy to identify for each file which is its license. I am not sure if this way is correct (anyway, it is the same way for dropzone and bootstap). Just to mention if somebody argues. > diff --git a/www2/README.md b/www2/README.md > new file mode 100644 > index 0000000..abb6199 > --- /dev/null > +++ b/www2/README.md > @@ -0,0 +1,31 @@ > +SWUpdate Web Page > +================= > + > +The SWUpdate web page uses the NPM package manager and depends on > +Bootstrap and DropzoneJS. > + > +It uses WebSocket for the asynchronous communication with the backend, > +allows a visualization of different image sources and automatically > +reloads the web page after a connection lost. > + > + > +Install > +------- > + > +```sh > +npm install > +``` > + > +Build > +----- > + > +```sh > +npm run build > +``` > + > +Lint > +---- > + > +```sh > +npm run lint > +``` I guess that this should be part of the documentation under doc/, better with a complete new chapter (web development or website). > diff --git a/www2/gulpfile.js b/www2/gulpfile.js As I said, we need some way to split between development and deployment. This file is for development, not for deployment. It could also be an additional target in Makefile, if things are easier. > new file mode 100755 > index 0000000..e740451 > --- /dev/null > +++ b/www2/gulpfile.js > @@ -0,0 +1,99 @@ > +var cleanCSS = require('gulp-clean-css') > +var del = require('del') > +var gulp = require('gulp') > +var favicons = require('gulp-favicons') > +var htmlmin = require('gulp-htmlmin') > +var htmlreplace = require('gulp-html-replace') > +var minify = require('gulp-minify') > +var rename = require('gulp-rename') > +var responsive = require('gulp-responsive') > + > +gulp.task('minify-css', ['clean'], () => { > + return gulp.src('recovery.css') > + .pipe(cleanCSS({ compatibility: 'ie8' })) > + .pipe(rename({ > + suffix: '.min' > + })) > + .pipe(gulp.dest('dist/css')) > +}) > + > +gulp.task('minify-js', ['clean'], () => { > + return gulp.src('recovery.js') > + .pipe(minify({ > + ext: { > + min: '.min.js' > + }, > + noSource: true > + })) > + .pipe(gulp.dest('dist/js')) > +}) > + > +gulp.task('minify-html', ['clean'], () => { > + return gulp.src('index.html') > + .pipe(htmlreplace({ > + 'js': ['js/jquery.min.js', 'js/bootstrap.min.js', 'js/dropzone.min.js', 'js/recovery.min.js'], > + 'css': ['css/bootstrap.min.css', 'css/dropzone.min.css', 'css/recovery.min.css'] > + })) > + .pipe(htmlmin({ collapseWhitespace: true })) > + .pipe(gulp.dest('dist')) > +}) > + > +gulp.task('copy-css', ['clean'], () => { > + return gulp.src([ > + 'node_modules/bootstrap/dist/css/bootstrap.min.css', > + 'node_modules/dropzone/dist/min/dropzone.min.css' > + ]) > + .pipe(gulp.dest('dist/css')) > +}) > + > +gulp.task('copy-js', ['clean'], () => { > + return gulp.src([ > + 'node_modules/jquery/dist/jquery.min.js', > + 'node_modules/bootstrap/dist/js/bootstrap.min.js', > + 'node_modules/dropzone/dist/min/dropzone.min.js' > + ]) > + .pipe(gulp.dest('dist/js')) > +}) > + > +gulp.task('copy-fonts', ['clean'], () => { > + return gulp.src([ > + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf', > + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff', > + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2' > + ]) > + .pipe(gulp.dest('dist/fonts')) > +}) > + > +gulp.task('resize-images', function () { > + return gulp.src('logo.png') > + .pipe(responsive({ > + 'logo.png': { > + height: 200 > + } > + })) > + .pipe(gulp.dest('dist')) > +}) > + > +gulp.task('favicons', ['clean'], () => { > + return gulp.src('logo.png').pipe(favicons({ > + icons: { > + android: false, > + appleIcon: false, > + appleStartup: false, > + coast: false, > + favicons: true, > + firefox: false, > + windows: false, > + yandex: false > + } > + })) > + .pipe(gulp.dest('dist')) > +}) > + > +gulp.task('clean', () => { > + return del('dist/**', { force: true }) > +}) > + > +gulp.task('build', ['copy-css', 'copy-js', 'copy-fonts', 'resize-images', 'minify-js', 'minify-css', 'minify-html', 'favicons']) > + > +gulp.task('default', ['build']) > diff --git a/www2/index.html b/www2/index.html > new file mode 100755 > index 0000000..1177081 > --- /dev/null > +++ b/www2/index.html > @@ -0,0 +1,133 @@ > +<!DOCTYPE html> > +<html lang="en"> > + > +<head> > + <meta charset="UTF-8"> > + <title>Firmware Update</title> > + > + <!-- build:css --> > + <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css"> > + <link rel="stylesheet" href="node_modules/dropzone/dist/min/dropzone.min.css"> > + <link rel="stylesheet" href="recovery.css"> > + <!-- endbuild --> > + > + <!-- build:js --> > + <script src="node_modules/jquery/dist/jquery.min.js"></script> > + <script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script> > + <script src="node_modules/dropzone/dist/min/dropzone.min.js"></script> > + <script src="recovery.js"></script> > + <!-- endbuild --> > +</head> > + > +<body> > + <!-- Use Bootstrap Grid Container --> > + <div class="container"> > + > + <div class="page-header"> > + <h1><img src="logo.png" height="50">SWUpdate</h1> > + </div> > + > + <div class="row"> > + <div class="col-lg-12 col-md-12 col-xs-12"> > + <legend> > + <h2> > + Firmware Update > + <div class="pull-right"> > + <label class="custom-control custom-checkbox label"> > + <input id="swu-auto-restart" class="form-check-input" type="checkbox" value=""><small> Restart system after update finished</small> > + </label> > + <button type="button" class="btn btn-primary" onclick="restart()"> > + <span class="glyphicon glyphicon-refresh"></span> Restart system > + </button> > + </div> > + </h2> > + </legend> > + </div> > + </div> > + > + <!-- Dropzone Button for drag&drop files --> > + <div class="row"> > + <div class="col-lg-12 col-md-12 col-xs-12"> > + <form class="dropzone panel" id="dropzone" action="/upload" method="post" enctype="multipart/form-data"> > + <div class="dz-default dz-message"> > + <span class="dz-message">Drop file here or click to upload a firmware update image.</span> > + </div> > + </form> > + <br> > + </div> > + </div> > + > + <!-- Warning --> > + <div class="row"> > + <div class="col-lg-12 col-md-12 col-xs-12"> > + <div id="swu-warning" class="alert alert-warning" hidden="hidden"> > + <span class="glyphicon glyphicon glyphicon-warning-sign"></span> Update firmware may take a few minutes, please don't turn off the power. > + </div> > + </div> > + </div> > + > + <!-- Progress Bar --> > + <div class="row"> > + <div id="swu-progress" class="col-lg-12 col-md-12 col-xs-12" hidden="hidden"> > + <div> > + <strong>Installing:</strong> > + <span id="swu-progress-name"></span> > + <span id="swu-progress-value" class="pull-right"></span> > + </div> > + <div class="progress"> > + <div id="swu-progress-bar" class="progress-bar" role="progressbar"></div> > + </div> > + </div> > + </div> > + > + <!-- Status --> > + <div class="row"> > + <div class="col-lg-12 col-md-12 col-xs-12"> > + <div id="swu-success" class="alert alert-success" hidden="hidden"> > + <span class="glyphicon glyphicon-ok"></span> Successful updated > + </div> > + <div id="swu-failure" class="alert alert-danger" hidden="hidden"> > + <span class="glyphicon glyphicon glyphicon-remove"></span> Update failed > + </div> > + </div> > + </div> > + > + <!-- Messages --> > + <div class="row"> > + <div class="col-lg-12 col-md-12 col-xs-12"> > + <div class="panel-group"> > + <div class="panel panel-default"> > + <div class="panel-heading"> > + <h4 class="panel-title"> > + <a class="collapsed" data-toggle="collapse" href="#collapse" aria-expanded="false">Details</a> > + </h4> > + </div> > + <div id="collapse" class="panel-collapse collapse"> > + <div class="panel-body" id="messages"></div> > + </div> > + </div> > + </div> > + </div> > + </div> > + > + <!-- Restart --> > + <div id="swu-restart" class="modal fade" tabindex="-1" role="dialog"> > + <div class="modal-dialog modal-sm" role="document"> > + <div class="modal-content"> > + <div class="modal-body"> > + <div class="row"> > + <div class="col-md-4"> > + <div class="loader"></div> > + </div> > + <div class="col-md-8"> > + <p>The system will restart. Please be patient, as restarting takes about one minute.</p> > + </div> > + </div> > + </div> > + </div> > + </div> > + </div> > + </div> > +</body> > + > +</html> > \ No newline at end of file > diff --git a/www2/logo.png b/www2/logo.png It is real a pity that the file is copied because it is already stored in repo. I would like to avoid this. > new file mode 100644 > index 0000000000000000000000000000000000000000..12ee4c41284a56a092499d4452641f6ef67fec83 > GIT binary patch > literal 24403 > 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 > > literal 0 > HcmV?d00001 > > diff --git a/www2/package.json b/www2/package.json > new file mode 100755 > index 0000000..4dc22e1 > --- /dev/null > +++ b/www2/package.json > @@ -0,0 +1,38 @@ > +{ > + "name": "swupdate", > + "version": "0.1.0", > + "description": "", > + "main": "index.html", > + "scripts": { > + "htmlhint": "htmlhint *.html", > + "standard": "standard --fix *.js", > + "lint": "standard && htmlhint", > + "build": "gulp", > + "clean": "gulp clean" > + }, > + "author": "", > + "license": "MIT", > + "devDependencies": { > + "del": "^3.0.0", > + "gulp": "^3.9.1", > + "gulp-clean-css": "^3.9.0", > + "gulp-favicons": "^2.2.7", > + "gulp-html-replace": "^1.6.2", > + "gulp-htmlmin": "^3.0.0", > + "gulp-minify": "^2.1.0", > + "gulp-rename": "^1.2.2", > + "gulp-responsive": "^2.8.0", > + "htmlhint": "^0.9.13", > + "standard": "^10.0.3" > + }, > + "dependencies": { > + "bootstrap": "^3.3.7", > + "dropzone": "5.2.0", > + "jquery": "^3.2.1" > + }, > + "standard": { > + "ignore": [ > + "dist" > + ] > + } > +} > diff --git a/www2/recovery.css b/www2/recovery.css > new file mode 100755 > index 0000000..3fe3492 > --- /dev/null > +++ b/www2/recovery.css > @@ -0,0 +1,36 @@ > +h1 img { > + margin-left: 10px; > + margin-right: 10px; > +} > + > +.dropzone .dz-preview .dz-image { > + width: 240px; > +} > + > +.loader { > + border: 10px solid LightGray; /* Light grey */ > + border-top: 10px solid Black; /* Blue */ > + border-bottom: 10px solid Black; /* Blue */ > + border-radius: 50%; > + width: 80px; > + height: 80px; > + animation: spin 2s linear infinite; > +} > + > +@keyframes spin { > + 0% { transform: rotate(0deg); } > + 100% { transform: rotate(360deg); } > +} > + > +.panel-group .panel-title>a:before { > + font-family: 'Glyphicons Halflings'; > + content: "\e114"; > + display: inline-block; > + font-size: 13px; > + margin-right: 8px; > + text-align: center; > +} > + > +.panel-group .panel-title>a.collapsed:before { > + content: "\e080"; > +} > diff --git a/www2/recovery.js b/www2/recovery.js > new file mode 100755 > index 0000000..9836831 > --- /dev/null > +++ b/www2/recovery.js > @@ -0,0 +1,110 @@ > +/* > + * Copyright (C) 2017 Weidmüller Interface GmbH & Co. KG > + * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> > + * > + * SPDX-License-Identifier: MIT > + */ > + > +/* global $, Dropzone, WebSocket */ > + > +function restart () { > + $.post('restart', {}, function (data) { > + showRestart() > + }) > +}; > + > +function showRestart () { > + $('#swu-restart').modal({backdrop: 'static', keyboard: false}) > + window.setTimeout(tryReload, 3000) > +} > +function tryReload () { > + $.ajax({ > + cache: false, > + timeout: 1000, > + success: function (response) { > + window.location.reload(true) > + }, > + error: function (xhr, textStatus, errorThrown) { > + if (textStatus === 'timeout') { tryReload() } > + window.setTimeout(tryReload, 1000) > + } > + }) > +} > + > +Dropzone.options.dropzone = { > + clickable: true, > + acceptedFiles: '.swu' > +} > + > +window.onload = function () { > + var protocol > + > + if (window.location.protocol === 'https:') { protocol = 'wss:' } else { protocol = 'ws:' } > + > + var ws = new WebSocket(protocol + '//' + window.location.host + '/ws') > + > + ws.onclose = function (event) { > + showRestart() > + } > + > + ws.onmessage = function (event) { > + var msg = JSON.parse(event.data) > + > + switch (msg.type) { > + case 'message': > + if (msg.level <= 3) { > + $('#messages').append('<p class="text-danger">' + msg.text + '</p>') > + } else { > + $('#messages').append('<p>' + msg.text + '</p>') > + } > + break > + case 'status': > + switch (msg.status) { > + case 'START': > + $('#swu-progress').hide() > + $('#swu-success').hide() > + $('#swu-failure').hide() > + $('#swu-warning').show() > + break > + case 'RUN': > + break > + case 'SUCCESS': > + $('#swu-progress').hide() > + $('#swu-warning').hide() > + $('#swu-failure').hide() > + $('#swu-success').show() > + if ($('#swu-auto-restart').prop('checked')) { restart() } > + break > + case 'FAILURE': > + $('#swu-progress').hide() > + $('#swu-warning').hide() > + $('#swu-success').hide() > + $('#swu-failure').show() > + break > + case 'DONE': > + $('#swu-progress').hide() > + $('#swu-warning').hide() > + $('#swu-success').hide() > + $('#swu-failure').hide() > + break > + default: > + console.log(msg) > + break > + } > + break > + case 'source': > + break > + case 'step': > + var percent = Math.round((100 * (Number(msg.step) - 1) + Number(msg.percent)) / Number(msg.number)) > + > + $('#swu-progress-value').html(percent + '%' + ' (' + msg.step + ' of ' + msg.number + ')') > + $('#swu-progress-name').html(msg.name) > + $('#swu-progress-bar') > + .css('width', percent + '%') > + .attr('aria-valuenow', percent) > + > + $('#swu-progress').show() > + break > + } > + } > +} > Anyway, from my first tests the website is quite impressive - a big improvement from current state ! Best regards, Stefano
Hi Stefano, > -----Ursprüngliche Nachricht----- > Von: Stefano Babic [mailto:sbabic@denx.de] > Gesendet: Donnerstag, 11. Januar 2018 09:50 > An: stefan@herbrechtsmeier.net; swupdate@googlegroups.com > Cc: Herbrechtsmeier Dr.-Ing. , Stefan > <Stefan.Herbrechtsmeier@weidmueller.com> > Betreff: Re: [swupdate] [PATCH 3/3] www: Add reworked web page with > bootstrap, dropzone and websocket support > > On 08/01/2018 11:48, stefan@herbrechtsmeier.net wrote: > > From: Stefan Herbrechtsmeier > <stefan.herbrechtsmeier@weidmueller.com> > > > > The new web page uses WebSocket for the asynchronous communication > with the > > backend, allows a visualization of different image sources and automatically > > reloads the web page after a connection lost. > > > > First at all, many thanks to provide a very responsive website, that > looks very professional and add features that are currently missing in > the simple website we currently have. It was at the beginning just an > example for website, it was never changed). > > I agree to store this in a separate directory, and users can choose > which website they want to have on target. I have some questions for my > understanding: Maybe we should even use a separate project repository. > using npm is straightforward, but I get errors because I had a old > version of nodejs. nodejs is not required on target, but for development > we should write somewhere which are the minimal requirements. Okay > My next issue is with deployment. "npm install" creates a big bunch of > packages that are not needed. As far as I can see in code (and from my > tests), it should be enough for the website to include : > > node_modules/bootstrap > node_modules/dropzone > node_modules/jquery > > Is there an easy way to deploy the website ? After this will be merged > here, recipes should be added for Yocto. You can generate the website with the "npm run build" this will generate a deploy version in the dict folder. > > Signed-off-by: Stefan Herbrechtsmeier > <stefan.herbrechtsmeier@weidmueller.com> > > --- > > www2/.gitignore | 2 + > > www2/LICENSE | 17 +++++++ > > www2/README.md | 31 +++++++++++++ > > www2/gulpfile.js | 99 > ++++++++++++++++++++++++++++++++++++++++ > > www2/index.html | 133 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > www2/logo.png | Bin 0 -> 24403 bytes > > www2/package.json | 38 ++++++++++++++++ > > www2/recovery.css | 36 +++++++++++++++ > > www2/recovery.js | 110 > ++++++++++++++++++++++++++++++++++++++++++++ > > 9 files changed, 466 insertions(+) > > create mode 100644 www2/.gitignore > > create mode 100644 www2/LICENSE > > create mode 100644 www2/README.md > > create mode 100755 www2/gulpfile.js > > create mode 100755 www2/index.html > > create mode 100644 www2/logo.png > > create mode 100755 www2/package.json > > create mode 100755 www2/recovery.css > > create mode 100755 www2/recovery.js > > > > diff --git a/www2/.gitignore b/www2/.gitignore > > new file mode 100644 > > index 0000000..1eae0cf > > --- /dev/null > > +++ b/www2/.gitignore > > @@ -0,0 +1,2 @@ > > +dist/ > > +node_modules/ > > diff --git a/www2/LICENSE b/www2/LICENSE > > new file mode 100644 > > index 0000000..fb950dc > > --- /dev/null > > +++ b/www2/LICENSE > > @@ -0,0 +1,17 @@ > > +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 NONINFRINGEMENT. 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. > For a FOSS project, it should be easy to identify for each file which is > its license. I am not sure if this way is correct (anyway, it is the > same way for dropzone and bootstap). Just to mention if somebody argues. Okay > > diff --git a/www2/README.md b/www2/README.md > > new file mode 100644 > > index 0000000..abb6199 > > --- /dev/null > > +++ b/www2/README.md > > @@ -0,0 +1,31 @@ > > +SWUpdate Web Page > > +================= > > + > > +The SWUpdate web page uses the NPM package manager and depends > on > > +Bootstrap and DropzoneJS. > > + > > +It uses WebSocket for the asynchronous communication with the > backend, > > +allows a visualization of different image sources and automatically > > +reloads the web page after a connection lost. > > + > > + > > +Install > > +------- > > + > > +```sh > > +npm install > > +``` > > + > > +Build > > +----- > > + > > +```sh > > +npm run build > > +``` > > + > > +Lint > > +---- > > + > > +```sh > > +npm run lint > > +``` > > I guess that this should be part of the documentation under doc/, better > with a complete new chapter (web development or website). You are right. This was only a first version to get some feedback. > > diff --git a/www2/gulpfile.js b/www2/gulpfile.js > > As I said, we need some way to split between development and > deployment. > This file is for development, not for deployment. > It could also be an additional target in Makefile, if things are easier. I would separate the web development. Maybe we could integrate only the deployment into this project or even better download a release from the website project. <snip> > Anyway, from my first tests the website is quite impressive - a big > improvement from current state ! Thank you and best regards Stefan Herbrechtsmeier Software Developer Embedded Systems Weidmüller – Your partner in Industrial Connectivity We look forward to sharing ideas with you – Let’s connect. Weidmueller Interface GmbH & Co. KG Klingenbergstraße 16, 32758 Detmold, Germany Email: Stefan.Herbrechtsmeier@weidmueller.com – Web: www.weidmueller.com
Hi Stefan, On 11/01/2018 10:02, Stefan.Herbrechtsmeier@weidmueller.com wrote: > Hi Stefano, > >> -----Ursprüngliche Nachricht----- >> Von: Stefano Babic [mailto:sbabic@denx.de] >> Gesendet: Donnerstag, 11. Januar 2018 09:50 >> An: stefan@herbrechtsmeier.net; swupdate@googlegroups.com >> Cc: Herbrechtsmeier Dr.-Ing. , Stefan >> <Stefan.Herbrechtsmeier@weidmueller.com> >> Betreff: Re: [swupdate] [PATCH 3/3] www: Add reworked web page with >> bootstrap, dropzone and websocket support >> >> On 08/01/2018 11:48, stefan@herbrechtsmeier.net wrote: >>> From: Stefan Herbrechtsmeier >> <stefan.herbrechtsmeier@weidmueller.com> >>> >>> The new web page uses WebSocket for the asynchronous communication >> with the >>> backend, allows a visualization of different image sources and automatically >>> reloads the web page after a connection lost. >>> >> >> First at all, many thanks to provide a very responsive website, that >> looks very professional and add features that are currently missing in >> the simple website we currently have. It was at the beginning just an >> example for website, it was never changed). >> >> I agree to store this in a separate directory, and users can choose >> which website they want to have on target. I have some questions for my >> understanding: > > Maybe we should even use a separate project repository. > This is another option, I am also thinking about of it. >> using npm is straightforward, but I get errors because I had a old >> version of nodejs. nodejs is not required on target, but for development >> we should write somewhere which are the minimal requirements. > > Okay > >> My next issue is with deployment. "npm install" creates a big bunch of >> packages that are not needed. As far as I can see in code (and from my >> tests), it should be enough for the website to include : >> >> node_modules/bootstrap >> node_modules/dropzone >> node_modules/jquery >> >> Is there an easy way to deploy the website ? After this will be merged >> here, recipes should be added for Yocto. > > You can generate the website with the "npm run build" this will generate a deploy version in the dict folder. Ok, thanks ! > >>> Signed-off-by: Stefan Herbrechtsmeier >> <stefan.herbrechtsmeier@weidmueller.com> >>> --- >>> www2/.gitignore | 2 + >>> www2/LICENSE | 17 +++++++ >>> www2/README.md | 31 +++++++++++++ >>> www2/gulpfile.js | 99 >> ++++++++++++++++++++++++++++++++++++++++ >>> www2/index.html | 133 >> ++++++++++++++++++++++++++++++++++++++++++++++++++++++ >>> www2/logo.png | Bin 0 -> 24403 bytes >>> www2/package.json | 38 ++++++++++++++++ >>> www2/recovery.css | 36 +++++++++++++++ >>> www2/recovery.js | 110 >> ++++++++++++++++++++++++++++++++++++++++++++ >>> 9 files changed, 466 insertions(+) >>> create mode 100644 www2/.gitignore >>> create mode 100644 www2/LICENSE >>> create mode 100644 www2/README.md >>> create mode 100755 www2/gulpfile.js >>> create mode 100755 www2/index.html >>> create mode 100644 www2/logo.png >>> create mode 100755 www2/package.json >>> create mode 100755 www2/recovery.css >>> create mode 100755 www2/recovery.js >>> >>> diff --git a/www2/.gitignore b/www2/.gitignore >>> new file mode 100644 >>> index 0000000..1eae0cf >>> --- /dev/null >>> +++ b/www2/.gitignore >>> @@ -0,0 +1,2 @@ >>> +dist/ >>> +node_modules/ >>> diff --git a/www2/LICENSE b/www2/LICENSE >>> new file mode 100644 >>> index 0000000..fb950dc >>> --- /dev/null >>> +++ b/www2/LICENSE >>> @@ -0,0 +1,17 @@ >>> +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 NONINFRINGEMENT. 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. >> For a FOSS project, it should be easy to identify for each file which is >> its license. I am not sure if this way is correct (anyway, it is the >> same way for dropzone and bootstap). Just to mention if somebody argues. > > Okay > >>> diff --git a/www2/README.md b/www2/README.md >>> new file mode 100644 >>> index 0000000..abb6199 >>> --- /dev/null >>> +++ b/www2/README.md >>> @@ -0,0 +1,31 @@ >>> +SWUpdate Web Page >>> +================= >>> + >>> +The SWUpdate web page uses the NPM package manager and depends >> on >>> +Bootstrap and DropzoneJS. >>> + >>> +It uses WebSocket for the asynchronous communication with the >> backend, >>> +allows a visualization of different image sources and automatically >>> +reloads the web page after a connection lost. >>> + >>> + >>> +Install >>> +------- >>> + >>> +```sh >>> +npm install >>> +``` >>> + >>> +Build >>> +----- >>> + >>> +```sh >>> +npm run build >>> +``` >>> + >>> +Lint >>> +---- >>> + >>> +```sh >>> +npm run lint >>> +``` >> >> I guess that this should be part of the documentation under doc/, better >> with a complete new chapter (web development or website). > > You are right. This was only a first version to get some feedback. > >>> diff --git a/www2/gulpfile.js b/www2/gulpfile.js >> >> As I said, we need some way to split between development and >> deployment. >> This file is for development, not for deployment. >> It could also be an additional target in Makefile, if things are easier. > > I would separate the web development. Maybe we could integrate only the deployment into this project or even better download a release from the website project. Agree, it looks to me the best way. We let development in SWUpdate repo, and I will create a separate repo for the website project to get release. This simplifies the integration into Yocto, too. > > <snip> > >> Anyway, from my first tests the website is quite impressive - a big >> improvement from current state ! > > Thank you and best regards Best regards, Stefano Babic
diff --git a/www2/.gitignore b/www2/.gitignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/www2/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/www2/LICENSE b/www2/LICENSE new file mode 100644 index 0000000..fb950dc --- /dev/null +++ b/www2/LICENSE @@ -0,0 +1,17 @@ +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 NONINFRINGEMENT. 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. diff --git a/www2/README.md b/www2/README.md new file mode 100644 index 0000000..abb6199 --- /dev/null +++ b/www2/README.md @@ -0,0 +1,31 @@ +SWUpdate Web Page +================= + +The SWUpdate web page uses the NPM package manager and depends on +Bootstrap and DropzoneJS. + +It uses WebSocket for the asynchronous communication with the backend, +allows a visualization of different image sources and automatically +reloads the web page after a connection lost. + + +Install +------- + +```sh +npm install +``` + +Build +----- + +```sh +npm run build +``` + +Lint +---- + +```sh +npm run lint +``` diff --git a/www2/gulpfile.js b/www2/gulpfile.js new file mode 100755 index 0000000..e740451 --- /dev/null +++ b/www2/gulpfile.js @@ -0,0 +1,99 @@ +var cleanCSS = require('gulp-clean-css') +var del = require('del') +var gulp = require('gulp') +var favicons = require('gulp-favicons') +var htmlmin = require('gulp-htmlmin') +var htmlreplace = require('gulp-html-replace') +var minify = require('gulp-minify') +var rename = require('gulp-rename') +var responsive = require('gulp-responsive') + +gulp.task('minify-css', ['clean'], () => { + return gulp.src('recovery.css') + .pipe(cleanCSS({ compatibility: 'ie8' })) + .pipe(rename({ + suffix: '.min' + })) + .pipe(gulp.dest('dist/css')) +}) + +gulp.task('minify-js', ['clean'], () => { + return gulp.src('recovery.js') + .pipe(minify({ + ext: { + min: '.min.js' + }, + noSource: true + })) + .pipe(gulp.dest('dist/js')) +}) + +gulp.task('minify-html', ['clean'], () => { + return gulp.src('index.html') + .pipe(htmlreplace({ + 'js': ['js/jquery.min.js', 'js/bootstrap.min.js', 'js/dropzone.min.js', 'js/recovery.min.js'], + 'css': ['css/bootstrap.min.css', 'css/dropzone.min.css', 'css/recovery.min.css'] + })) + .pipe(htmlmin({ collapseWhitespace: true })) + .pipe(gulp.dest('dist')) +}) + +gulp.task('copy-css', ['clean'], () => { + return gulp.src([ + 'node_modules/bootstrap/dist/css/bootstrap.min.css', + 'node_modules/dropzone/dist/min/dropzone.min.css' + ]) + .pipe(gulp.dest('dist/css')) +}) + +gulp.task('copy-js', ['clean'], () => { + return gulp.src([ + 'node_modules/jquery/dist/jquery.min.js', + 'node_modules/bootstrap/dist/js/bootstrap.min.js', + 'node_modules/dropzone/dist/min/dropzone.min.js' + ]) + .pipe(gulp.dest('dist/js')) +}) + +gulp.task('copy-fonts', ['clean'], () => { + return gulp.src([ + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf', + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff', + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2' + ]) + .pipe(gulp.dest('dist/fonts')) +}) + +gulp.task('resize-images', function () { + return gulp.src('logo.png') + .pipe(responsive({ + 'logo.png': { + height: 200 + } + })) + .pipe(gulp.dest('dist')) +}) + +gulp.task('favicons', ['clean'], () => { + return gulp.src('logo.png').pipe(favicons({ + icons: { + android: false, + appleIcon: false, + appleStartup: false, + coast: false, + favicons: true, + firefox: false, + windows: false, + yandex: false + } + })) + .pipe(gulp.dest('dist')) +}) + +gulp.task('clean', () => { + return del('dist/**', { force: true }) +}) + +gulp.task('build', ['copy-css', 'copy-js', 'copy-fonts', 'resize-images', 'minify-js', 'minify-css', 'minify-html', 'favicons']) + +gulp.task('default', ['build']) diff --git a/www2/index.html b/www2/index.html new file mode 100755 index 0000000..1177081 --- /dev/null +++ b/www2/index.html @@ -0,0 +1,133 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <title>Firmware Update</title> + + <!-- build:css --> + <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css"> + <link rel="stylesheet" href="node_modules/dropzone/dist/min/dropzone.min.css"> + <link rel="stylesheet" href="recovery.css"> + <!-- endbuild --> + + <!-- build:js --> + <script src="node_modules/jquery/dist/jquery.min.js"></script> + <script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script> + <script src="node_modules/dropzone/dist/min/dropzone.min.js"></script> + <script src="recovery.js"></script> + <!-- endbuild --> +</head> + +<body> + <!-- Use Bootstrap Grid Container --> + <div class="container"> + + <div class="page-header"> + <h1><img src="logo.png" height="50">SWUpdate</h1> + </div> + + <div class="row"> + <div class="col-lg-12 col-md-12 col-xs-12"> + <legend> + <h2> + Firmware Update + <div class="pull-right"> + <label class="custom-control custom-checkbox label"> + <input id="swu-auto-restart" class="form-check-input" type="checkbox" value=""><small> Restart system after update finished</small> + </label> + <button type="button" class="btn btn-primary" onclick="restart()"> + <span class="glyphicon glyphicon-refresh"></span> Restart system + </button> + </div> + </h2> + </legend> + </div> + </div> + + <!-- Dropzone Button for drag&drop files --> + <div class="row"> + <div class="col-lg-12 col-md-12 col-xs-12"> + <form class="dropzone panel" id="dropzone" action="/upload" method="post" enctype="multipart/form-data"> + <div class="dz-default dz-message"> + <span class="dz-message">Drop file here or click to upload a firmware update image.</span> + </div> + </form> + <br> + </div> + </div> + + <!-- Warning --> + <div class="row"> + <div class="col-lg-12 col-md-12 col-xs-12"> + <div id="swu-warning" class="alert alert-warning" hidden="hidden"> + <span class="glyphicon glyphicon glyphicon-warning-sign"></span> Update firmware may take a few minutes, please don't turn off the power. + </div> + </div> + </div> + + <!-- Progress Bar --> + <div class="row"> + <div id="swu-progress" class="col-lg-12 col-md-12 col-xs-12" hidden="hidden"> + <div> + <strong>Installing:</strong> + <span id="swu-progress-name"></span> + <span id="swu-progress-value" class="pull-right"></span> + </div> + <div class="progress"> + <div id="swu-progress-bar" class="progress-bar" role="progressbar"></div> + </div> + </div> + </div> + + <!-- Status --> + <div class="row"> + <div class="col-lg-12 col-md-12 col-xs-12"> + <div id="swu-success" class="alert alert-success" hidden="hidden"> + <span class="glyphicon glyphicon-ok"></span> Successful updated + </div> + <div id="swu-failure" class="alert alert-danger" hidden="hidden"> + <span class="glyphicon glyphicon glyphicon-remove"></span> Update failed + </div> + </div> + </div> + + <!-- Messages --> + <div class="row"> + <div class="col-lg-12 col-md-12 col-xs-12"> + <div class="panel-group"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h4 class="panel-title"> + <a class="collapsed" data-toggle="collapse" href="#collapse" aria-expanded="false">Details</a> + </h4> + </div> + <div id="collapse" class="panel-collapse collapse"> + <div class="panel-body" id="messages"></div> + </div> + </div> + </div> + </div> + </div> + + <!-- Restart --> + <div id="swu-restart" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog modal-sm" role="document"> + <div class="modal-content"> + <div class="modal-body"> + <div class="row"> + <div class="col-md-4"> + <div class="loader"></div> + </div> + <div class="col-md-8"> + <p>The system will restart. Please be patient, as restarting takes about one minute.</p> + </div> + </div> + </div> + </div> + </div> + </div> + </div> +</body> + +</html> \ No newline at end of file diff --git a/www2/logo.png b/www2/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..12ee4c41284a56a092499d4452641f6ef67fec83 GIT binary patch literal 24403 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 literal 0 HcmV?d00001 diff --git a/www2/package.json b/www2/package.json new file mode 100755 index 0000000..4dc22e1 --- /dev/null +++ b/www2/package.json @@ -0,0 +1,38 @@ +{ + "name": "swupdate", + "version": "0.1.0", + "description": "", + "main": "index.html", + "scripts": { + "htmlhint": "htmlhint *.html", + "standard": "standard --fix *.js", + "lint": "standard && htmlhint", + "build": "gulp", + "clean": "gulp clean" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "del": "^3.0.0", + "gulp": "^3.9.1", + "gulp-clean-css": "^3.9.0", + "gulp-favicons": "^2.2.7", + "gulp-html-replace": "^1.6.2", + "gulp-htmlmin": "^3.0.0", + "gulp-minify": "^2.1.0", + "gulp-rename": "^1.2.2", + "gulp-responsive": "^2.8.0", + "htmlhint": "^0.9.13", + "standard": "^10.0.3" + }, + "dependencies": { + "bootstrap": "^3.3.7", + "dropzone": "5.2.0", + "jquery": "^3.2.1" + }, + "standard": { + "ignore": [ + "dist" + ] + } +} diff --git a/www2/recovery.css b/www2/recovery.css new file mode 100755 index 0000000..3fe3492 --- /dev/null +++ b/www2/recovery.css @@ -0,0 +1,36 @@ +h1 img { + margin-left: 10px; + margin-right: 10px; +} + +.dropzone .dz-preview .dz-image { + width: 240px; +} + +.loader { + border: 10px solid LightGray; /* Light grey */ + border-top: 10px solid Black; /* Blue */ + border-bottom: 10px solid Black; /* Blue */ + border-radius: 50%; + width: 80px; + height: 80px; + animation: spin 2s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.panel-group .panel-title>a:before { + font-family: 'Glyphicons Halflings'; + content: "\e114"; + display: inline-block; + font-size: 13px; + margin-right: 8px; + text-align: center; +} + +.panel-group .panel-title>a.collapsed:before { + content: "\e080"; +} diff --git a/www2/recovery.js b/www2/recovery.js new file mode 100755 index 0000000..9836831 --- /dev/null +++ b/www2/recovery.js @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2017 Weidmüller Interface GmbH & Co. KG + * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> + * + * SPDX-License-Identifier: MIT + */ + +/* global $, Dropzone, WebSocket */ + +function restart () { + $.post('restart', {}, function (data) { + showRestart() + }) +}; + +function showRestart () { + $('#swu-restart').modal({backdrop: 'static', keyboard: false}) + window.setTimeout(tryReload, 3000) +} +function tryReload () { + $.ajax({ + cache: false, + timeout: 1000, + success: function (response) { + window.location.reload(true) + }, + error: function (xhr, textStatus, errorThrown) { + if (textStatus === 'timeout') { tryReload() } + window.setTimeout(tryReload, 1000) + } + }) +} + +Dropzone.options.dropzone = { + clickable: true, + acceptedFiles: '.swu' +} + +window.onload = function () { + var protocol + + if (window.location.protocol === 'https:') { protocol = 'wss:' } else { protocol = 'ws:' } + + var ws = new WebSocket(protocol + '//' + window.location.host + '/ws') + + ws.onclose = function (event) { + showRestart() + } + + ws.onmessage = function (event) { + var msg = JSON.parse(event.data) + + switch (msg.type) { + case 'message': + if (msg.level <= 3) { + $('#messages').append('<p class="text-danger">' + msg.text + '</p>') + } else { + $('#messages').append('<p>' + msg.text + '</p>') + } + break + case 'status': + switch (msg.status) { + case 'START': + $('#swu-progress').hide() + $('#swu-success').hide() + $('#swu-failure').hide() + $('#swu-warning').show() + break + case 'RUN': + break + case 'SUCCESS': + $('#swu-progress').hide() + $('#swu-warning').hide() + $('#swu-failure').hide() + $('#swu-success').show() + if ($('#swu-auto-restart').prop('checked')) { restart() } + break + case 'FAILURE': + $('#swu-progress').hide() + $('#swu-warning').hide() + $('#swu-success').hide() + $('#swu-failure').show() + break + case 'DONE': + $('#swu-progress').hide() + $('#swu-warning').hide() + $('#swu-success').hide() + $('#swu-failure').hide() + break + default: + console.log(msg) + break + } + break + case 'source': + break + case 'step': + var percent = Math.round((100 * (Number(msg.step) - 1) + Number(msg.percent)) / Number(msg.number)) + + $('#swu-progress-value').html(percent + '%' + ' (' + msg.step + ' of ' + msg.number + ')') + $('#swu-progress-name').html(msg.name) + $('#swu-progress-bar') + .css('width', percent + '%') + .attr('aria-valuenow', percent) + + $('#swu-progress').show() + break + } + } +}