diff --git a/Server/flask_test.py b/Server/flask_test.py new file mode 100644 index 0000000..708fef9 --- /dev/null +++ b/Server/flask_test.py @@ -0,0 +1,55 @@ +import threading +import atexit +from flask import Flask +from server import * + +POOL_TIME = 0 # Seconds + + +class ServerThread(threading.Thread): + def run(self): + server = Server() + server.start() + while True: + pass + + +# variables that are accessible from anywhere +commonDataStruct = {} +# lock to control access to variable +dataLock = threading.Lock() +# thread handler +yourThread = ServerThread() + + +def create_app(): + app = Flask(__name__) + + def interrupt(): + global yourThread + yourThread.cancel() + + def doStuff(): + global commonDataStruct + global yourThread + with dataLock: + print('kek') + yourThread = threading.Timer(POOL_TIME, doStuff, ()) + yourThread.start() + + def doStuffStart(): + # Do initialisation stuff here + global yourThread + # Create your thread + yourThread = threading.Timer(POOL_TIME, doStuff, ()) + yourThread.start() + + # Initiate + doStuffStart() + # When you kill Flask (SIGTERM), clear the trigger for the next thread + atexit.register(interrupt) + return app + + +app = create_app() +app.run(host='0.0.0.0', debug=True) diff --git a/Server/static/css/main.css b/Server/static/css/main.css index a84335e..83cb348 100644 --- a/Server/static/css/main.css +++ b/Server/static/css/main.css @@ -1,19 +1,24 @@ -html, body, .container { +html, body, .my-container { width: 100%; height: 100%; margin: 0; padding: 0; } -body { - padding: 20px; +html { + height: calc(100% - 56px); +} + +.my-container { display: flex; flex-direction: row; + width: 100%; } .table-container { - height: 100%; - width: 70%; + height: calc(100% - 40px); + width: calc(70% - 40px); + padding: 20px; } .button { @@ -29,8 +34,18 @@ body { display: flex; flex-direction: row; flex-wrap: wrap; + padding: 0 0 15px 20px; } -.action-container button { - margin: 10px; +.action-container .my-label { + margin-top: 20px; +} + +.action-container .btn-group { + padding-top: 20px; + width: 100%; +} + +.btn-group button { + width: 100%; } \ No newline at end of file diff --git a/Server/static/css/spinner.css b/Server/static/css/spinner.css new file mode 100644 index 0000000..0014138 --- /dev/null +++ b/Server/static/css/spinner.css @@ -0,0 +1,93 @@ +.lds-spinner { + color: official; + display: inline-block; + position: relative; + width: 40px; + height: 40px; +} + +.lds-spinner div { + transform-origin: 20px 20px; + animation: lds-spinner 1.2s linear infinite; +} + +.lds-spinner div:after { + content: " "; + display: block; + position: absolute; + top: 5px; + left: 25px; + width: 3px; + height: 10px; + border-radius: 20%; + background: #fff; +} + +.lds-spinner div:nth-child(1) { + transform: rotate(0deg); + animation-delay: -1.1s; +} + +.lds-spinner div:nth-child(2) { + transform: rotate(30deg); + animation-delay: -1s; +} + +.lds-spinner div:nth-child(3) { + transform: rotate(60deg); + animation-delay: -0.9s; +} + +.lds-spinner div:nth-child(4) { + transform: rotate(90deg); + animation-delay: -0.8s; +} + +.lds-spinner div:nth-child(5) { + transform: rotate(120deg); + animation-delay: -0.7s; +} + +.lds-spinner div:nth-child(6) { + transform: rotate(150deg); + animation-delay: -0.6s; +} + +.lds-spinner div:nth-child(7) { + transform: rotate(180deg); + animation-delay: -0.5s; +} + +.lds-spinner div:nth-child(8) { + transform: rotate(210deg); + animation-delay: -0.4s; +} + +.lds-spinner div:nth-child(9) { + transform: rotate(240deg); + animation-delay: -0.3s; +} + +.lds-spinner div:nth-child(10) { + transform: rotate(270deg); + animation-delay: -0.2s; +} + +.lds-spinner div:nth-child(11) { + transform: rotate(300deg); + animation-delay: -0.1s; +} + +.lds-spinner div:nth-child(12) { + transform: rotate(330deg); + animation-delay: 0s; +} + +@keyframes lds-spinner { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} diff --git a/Server/static/js/file.js b/Server/static/js/file.js new file mode 100644 index 0000000..4265cc8 --- /dev/null +++ b/Server/static/js/file.js @@ -0,0 +1,55 @@ +let animationInput = document.getElementById('animationFile'); +let configInput = document.getElementById('configFile'); +let arucoInput = document.getElementById('arucoFile'); + +animationInput.onchange = function (e) { + document.getElementById('animationFileLabel').innerText = animationInput.files[0].name; +}; +configInput.onchange = function (e) { + document.getElementById('configFileLabel').innerText = configInput.files[0].name; +}; +arucoInput.onchange = function (e) { + document.getElementById('arucoFileLabel').innerText = arucoInput.files[0].name; +}; + +function sendRows(selectedRows) { + var animationFile = animationInput.files[0]; + var configFile = configInput.files[0]; + var arucoFile = arucoInput.files[0]; + spinner.style.display = 'inline-block'; + setTimeout(function () { + selectedRows.forEach(function (element) { + if (animationFile) { + let animReq = new XMLHttpRequest(); + let animFormData = new FormData(); + animFormData.append("file", animationFile); + animReq.open("POST", '/set/animation?ip=' + element._row.data.ip, false); + animReq.send(animFormData); + } + if (configFile) { + let configReq = new XMLHttpRequest(); + let congifFormData = new FormData(); + congifFormData.append("file", configFile); + configReq.open("POST", '/set/config?ip=' + element._row.data.ip, false); + configReq.send(congifFormData); + } + if (arucoFile) { + let arucoReq = new XMLHttpRequest(); + let arucoFormData = new FormData(); + arucoFormData.append("file", arucoFile); + arucoReq.open("POST", '/set/animation?ip=' + element._row.data.ip, false); + arucoReq.send(arucoFormData); + } + element.deselect(); + }); + spinner.style.display = 'none'; + }, 20); +} + +function sendSelected() { + sendRows(table.getSelectedRows()); +} + +function sendAll() { + sendRows(table.getRows()); +} \ No newline at end of file diff --git a/Server/static/js/main.js b/Server/static/js/main.js index a7258ec..9e91f28 100644 --- a/Server/static/js/main.js +++ b/Server/static/js/main.js @@ -1,3 +1,4 @@ +let spinner = document.getElementById('spinner'); var tabledata = []; updateData(); @@ -11,6 +12,7 @@ function updateData() { var table = new Tabulator("#copters-table", { data: tabledata, reactiveData: true, + selectable: true, layout: "fitColumns", columns: [ {title: "Name", field: "name"}, @@ -22,3 +24,28 @@ var table = new Tabulator("#copters-table", { {title: "Time", field: "time"}, ], }); + +function refreshRows(selectedRows) { + spinner.style.display = 'inline-block'; + setTimeout(function () { + selectedRows.forEach(function (element) { + let req = new XMLHttpRequest(); + req.open('POST', '/selfcheck/selected?ip=' + element._row.data.ip, false); + req.send(); + element.deselect(); + let response = JSON.parse(req.response); + Object.keys(response).forEach(function (item) { + element._row.data[item] = response[item]; + }); + }); + spinner.style.display = 'none'; + }, 20); +} + +function refreshSelected() { + refreshRows(table.getSelectedRows()); +} + +function refreshAll() { + refreshRows(table.getRows()); +} \ No newline at end of file diff --git a/Server/templates/main.html b/Server/templates/main.html index 3da0ec6..a51c576 100644 --- a/Server/templates/main.html +++ b/Server/templates/main.html @@ -2,24 +2,69 @@ - Main + Clever Show + -
-
-
-
-
- - - + +
+
+
+
+
+
+
+ + +
+
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+
+
+ +
- \ No newline at end of file diff --git a/Server/web_server.py b/Server/web_server.py index 0c4ebc7..c204ec2 100644 --- a/Server/web_server.py +++ b/Server/web_server.py @@ -7,16 +7,11 @@ app = Flask(__name__, static_url_path='/static') copters = [] -def response_handler(res, name, ip): - print('\n\n\n', res, '\n\n\n', name, '\n\n\n', ip, '\n\n\n') - return 1 - - @app.route('/') def home(): data = dict() data['clients'] = [] - # refresh_copters_list() + refresh_copters() for client in Client.clients.keys(): data['clients'].append([client, Client.clients[client].copter_id]) return render_template('main.html', data=data) @@ -37,7 +32,7 @@ def refresh_copters(): return jsonify({'m': 'Error'}) -@app.route('/selfcheck/selected') +@app.route('/selfcheck/selected', methods=["GET", "POST"]) def selfcheck_selected(): data = dict() ip = request.args.get("ip") @@ -52,6 +47,8 @@ def selfcheck_selected(): 'time': copter.time, 'name': copter.name, } + data = {"anim_id": "No animation", "batt_voltage": 3.259999990463257, "cell_voltage": 1.0850000381469727, + "ip": "192.168.43.31", "name": "CLever7", "selfcheck": "OK", "time": 1554723269.57106} return jsonify(data) @@ -69,9 +66,36 @@ def selfcheck_all(): 'time': copter.time, 'name': copter.name, }) + data = [{"anim_id": "No animation", "batt_voltage": 4.259999990463257, "cell_voltage": 4.0850000381469727, + "ip": "192.168.43.31", "name": "CLever7", "selfcheck": "OK", "time": 4554723269.57106}] + data *= 12 return jsonify(data) +@app.route('/set/animation', methods=['GET', 'POST']) +def set_animation(): + if request.method == 'POST': + f = request.files['file'] + print(f, 'ip', request.args.get('ip')) + return jsonify({'m': 'ok'}) + + +@app.route('/set/config', methods=['GET', 'POST']) +def set_config(): + if request.method == 'POST': + f = request.files['file'] + print(f, 'ip', request.args.get('ip')) + return jsonify({'m': 'ok'}) + + +@app.route('/set/aruco', methods=['GET', 'POST']) +def set_aruco(): + if request.method == 'POST': + f = request.files['file'] + print(f, 'ip', request.args.get('ip')) + return jsonify({'m': 'ok'}) + + class ServerThread(threading.Thread): def run(self): server = Server() @@ -80,6 +104,6 @@ class ServerThread(threading.Thread): pass -# server_thread = ServerThread() -# server_thread.start() +server_thread = ServerThread() +server_thread.start() app.run(host='0.0.0.0', debug=False)