Added file transfer from web to flask server

This commit is contained in:
Igor Nurullaev
2019-08-28 18:21:02 +03:00
parent 30ea2d6ab3
commit 6c3af9382d
7 changed files with 341 additions and 27 deletions

55
Server/flask_test.py Normal file
View File

@@ -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)

View File

@@ -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%;
}

View File

@@ -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;
}
}

55
Server/static/js/file.js Normal file
View File

@@ -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());
}

View File

@@ -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());
}

View File

@@ -2,24 +2,69 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Main</title>
<title>Clever Show </title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/tabulator.min.css">
<link rel="stylesheet" href="/static/css/spinner.css">
<link rel="stylesheet" href="/static/css/main.css">
<script lang="js" src="/static/js/tabulator.min.js"></script>
</head>
<body>
<div class="table-container">
<div id="copters-table"></div>
</div>
<div class="command-container col">
<div class="action-container">
<button type="button" class="btn btn-dark">Set animation</button>
<button type="button" class="btn btn-dark">Set configuration</button>
<button type="button" class="btn btn-dark">Set aruco map</button>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top"
style=" width: 100%; display: flex; flex-direction: row; justify-content: space-between;">
<a href="" class="navbar-brand">Clever Show</a>
<div style="display: none;" id="spinner" class="lds-spinner">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="action-container"></div>
</nav>
<div class="my-container">
<div class="table-container">
<div id="copters-table"></div>
</div>
<div class="command-container">
<div class="action-container">
<div class="btn-group">
<button type="button" class="btn btn-primary btn-lg" onclick="refreshSelected()">Refresh selected
</button>
<button type="button" class="btn btn-secondary btn-lg" onclick="refreshAll()">Refresh all</button>
</div>
</div>
<div class="action-container">
<label>Set animation</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="animationFile">
<label id="animationFileLabel" class="custom-file-label" for="animationFile">Choose file</label>
</div>
<label class="my-label">Set configuration</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="configFile">
<label id="configFileLabel" class="custom-file-label" for="configFile">Choose file</label>
</div>
<label class="my-label">Set ArUco map</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="arucoFile">
<label id="arucoFileLabel" class="custom-file-label" for="arucoFile">Choose file</label>
</div>
<div class="btn-group">
<button type="button" class="btn btn-primary btn-lg" onclick="sendSelected()">Save to selected</button>
<button type="button" class="btn btn-secondary btn-lg" onclick="sendAll()">Save to all</button>
</div>
</div>
<div class="action-container"></div>
</div>
<script lang="js" src="/static/js/main.js"></script>
<script lang="js" src="/static/js/file.js"></script>
</div>
<script lang="js" src="/static/js/main.js"></script>
</body>
</html>

View File

@@ -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)