Flask event stream app with js eventsource overwrites previous stream output - javascript

My flask app:
import eventlet
eventlet.monkey_patch()
from flask import Flask, render_template, Response
from shelljob import proc
from time import sleep
app = Flask(__name__)
#app.route('/')
def index():
return render_template('stream.html')
#app.route('/stream')
def stream():
g = proc.Group()
p = g.run([ "bash", "-c", "sudo tcpdump -i ens4 -s 0 -nX tcp port 5000" ])
def read_process():
while g.is_pending():
lines = g.readlines()
for proc, line in lines:
yield "data:" + str(line) + "\n\n"
return Response(read_process(), mimetype='text/event-stream')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)
stream.html:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Output</h1>
<div id="dumps"></div>
</body>
<script>
var targetContainer = document.getElementById("dumps");
var eventSource = new EventSource("/stream");
eventSource.onmessage = function(e) {
targetContainer.innerHTML = e.data;
};
</script>
</html>
My issue:
The outputs seems to get to the browser but any new streamed output overwrites the previous one. For e.g. the following is all i see in the browser and the line keeps changing as stream is updated:
Output
b'\t0x0020: 8011 01fa ce65 0000 0101 080a 1984 6ce7 .....e........l.\n' <=== keeps updating
What i'd like to achieve is a stream of tcpdump output rendered on my browser as an actual bash output would look like.
This works fine if i point to url path /stream and the reason why i'm embedding into html is to be able to give it some style.

Changed to targetContainer.innerHTML += e.data + "<br/> in <script>

Related

How to display changes made in a log file on a webpage using Flask and Javascript?

I have made a flask app that detects the changes made in a log file like the tail-f command in UNIX, but when I run it and make changes in the log file the output is not displayed on the webpage, I have written the code with reference to this,
Here is my flask app
import time
import os
from flask import Flask, render_template
app=Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/logs')
def logs():
def generate():
with open("log.log") as f:
while True:
# read last line of file
line = f.readline()
# sleep if file hasn't been updated
if not line:
time.sleep(0.1)
continue
yield line
return app.response_class(generate(), mimetype='text/plain')
app.run(debug=True)
Here is the log file, just for the sake of simplicity I have created a dummy log file
like this
1
2
3
4
5
and here is the index.html file
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<pre id="output"></pre>
<meta charset="utf-8">
<title>Logs</title>
<p>LOGS</p>
<script>
var output = document.getElementById('output');
var xhr = new XMLHttpRequest();
xhr.open('GET', '{{ url_for('logs') }}');
xhr.send();
setInterval(function() {
output.textContent = xhr.responseText;
}, 1000);
</script>
</body>
</html>
Now when I run this Flask App nothing is diplayed on the localhost server, what am I doing wrong here, so that I can get the logs displayed without refreshing the webpage?
In my opinion you should use a reader to read the stream. This means that the end of the transmission is not waited for, but is read in piece by piece.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<pre id="output"></pre>
<script type="text/javascript">
(() => {
fetch('/logs')
.then(response => {
const elem = document.getElementById('output');
const reader = response.body.getReader();
const readStream = ({ done,value }) => {
if (done) {
return;
}
let chunk = String.fromCharCode.apply(null, value);
elem.textContent += chunk + '\n';
return reader.read().then(readStream);
};
reader.read().then(readStream);
});
})();
</script>
</body>
</html>

Flask SocketIO messages not received

I am new to Flask and the IO. I'm trying to implement a basic data receiver for a machine with RoS. I have a python script sending data to a web server running on Flask.
The issue is that the java-script callback is never called and thus the numbers are strangely never added to the list, even though there are no errors.
The relevant parts are this:
Within Python Webserver:
#socketio.on('connect', namespace='/test')
def test_connect():
print("CALLED")
#socketio.on('disconnect', namespace='/test')
def test_disconnect():
print('Client disconnected')
It is supposed to post a random number defined by the following Ros Callback function (This is called every 0.5s):
def forklift_callback(self, msg):
#SOCKET TEST
number = random.randint(1,101)
print(number)
socketio.emit('newnumber', {'number': number}, namespace='/test')
Perhaps the way I define it is important:
*import statements*
from std_msgs.msg import String, UInt32
from umd_msgs.msg import PCNotification, RoverStatus, Barcode
mod = Blueprint('api', __name__)
msg = String()
started = "robot started"
socketio = SocketIO(app)
... All functionality ...
if __name__ == '__main__':
socketio.run(app)
This is connected to the html page at the following location:
<html>
<head>
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script>
$(document).ready(function(){
//connect to the socket server.
console.log('http://' + document.domain + ':' + location.port + '/test')
var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
var numbers_received = [];
//receive details from server
socket.on('newnumber', function(msg) {
console.log("Blank")
console.log("Received number" + msg.number);
//maintain a list of ten numbers
if (numbers_received.length >= 10){
numbers_received.shift()
}
numbers_received.push(msg.number);
numbers_string = '';
for (var i = 0; i < numbers_received.length; i++){
numbers_string = numbers_string + '<p>' + numbers_received[i].toString() + '</p>';
}
$('#log').html(numbers_string);
});
});
</script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<div class="jumbotron">
<h1>Asynchronous Flask Communication</h1>
<p>Random numbers generated by the Flask server will appear below, asynchronously.</p>
</div>
</div>
</div>
<div class="container" id="content">
<div class="row">
<p>Asynchronous page updates will appear here:</p>
<h3>Number list:</h3>
<div id="log">
</div> <!-- /#log -->
</div>
</div>
Try somethin like this,maybe it helps
You can do it with Thread that will run your function
yourThread = Thread()
thread_stop_event = Event()
class someClass(Thread):
def __init__(self):
self.delay = 1
super(someClass,self).__init__()
def forklift_callback(self):
while not thread_stop_event.isSet():
number = random.randint(1,101)
print(number)
socketio.emit('newnumber', {'number': number}, namespace='/test')
sleep(self.delay)
def run(self):
self.forklift_callback()
#socketio.on('connect', namespace='/test')
def test_connect():
print("CALLED")
global yourThread
if(not yourThread.isAlive()):
yourThread = someClass()
yourThread.start()
or just run it after client connects your server
#socketio.on('connect', namespace='/test')
def test_connect():
//your function here
print("CALLED")

Is there way to change the value of a variable in a python script from javascript?

I have a python script and apache web server running in a raspberry pi. I want to change the value of a variable in my python script from a web page using javascript. It is possible?
We can use socket. for easily using socket, we can use socket.io
Start.html
<!doctype html>
<html>
<head>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
</head>
<body>
<h1>Socket.IO GPIO control</h1>
<button id="btnGpio">Change GPIO</button>
<script>
var socket = io.connect('http://localhost:5000');
var index = 0;
socket.on('connect', function () {
console.log('connected')
document.getElementById('btnGpio').addEventListener('click', () => {
index = index + 1;
console.log('index', index)
socket.emit('change_gpio', { status: (index % 2 == 0) })
})
});
</script>
</body>
</html>
socket_server.py
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
#app.route("/")
def home():
return render_template("Start.html")
socketio = SocketIO(app)
pin = True
#socketio.on('change_gpio')
def handle_my_custom_event(json):
pin = json['status']
print('pin = ' , pin)
#socketio.on('connect', namespace='/')
def test_connect():
print('Connected')
if __name__ == '__main__':
socketio.run(app)
Install library with pip
pip install flask
pip install flask-socketio
Document:
https://flask-socketio.readthedocs.io/en/latest/

Why is my WebSocket automatically closing using Flask?

I've set up a very basic websocket server using flask.
websocket.py
from flask import Flask
from flask_uwsgi_websocket import GeventWebSocket
app = Flask(__name__)
ws = GeventWebSocket(app)
#app.route('/')
def index():
return render_template('index.html')
#ws.route('/foobar')
def echo(wscon):
msg = wscon.receive()
if msg is not None:
wscon.send(msg)
if __name__ == '__main__':
app.run(gevent=1000, host='0.0.0.0', port=9090)
index.html
<html>
<head>
<script language="Javascript">
var s = new WebSocket("ws://192.168.3.49:9090/foobar");
s.onopen = function() {
alert("connected !!!");
s.send("js send to server");
};
s.onmessage = function(e) {
alert("recv message")
var bb = document.getElementById('blackboard')
var html = bb.innerHTML;
bb.innerHTML = html + '<br/>' + e.data;
};
s.onerror = function(e) {
alert('error');
alert(e);
}
s.onclose = function(e) {
alert("connection closed");
}
function invia() {
var value = document.getElementById('testo').value;
alert(value);
s.send(value);
}
</script>
</head>
<body>
<h1>WebSocket</h1>
<input type="text" id="testo"/>
<input type="button" value="invia" onClick="invia();"/>
<div id="blackboard" style="width:640px;height:480px;background-color:black;color:white;border: solid 2px red;overflow:auto">
</div>
</body>
when I access http://ip:9090, I get the blow information:
connected !!!
recv message
connection closed
why websocket auto close? And occasionally there will be an error
[uwsgi-http key: 192.168.3.49:9090 client_addr: 192.168.3.1
client_port: 9177] hr_instance_read(): Connection reset by peer
[plugins/http/http.c line 646]
Seems like your are trying for echo gevent server. Example code
You need to keep the connection running by a loop. Change as following:
#ws.route('/foobar')
def echo(ws):
while True:
msg = ws.receive()
print(msg)
if msg is not None:
ws.send(msg)
else:
return

get multiple variables from form with websockets in python

Just starting to learn websockets and python. for now I am using Socket-IO and have the very basic 'pull from form and echo it back out' but I need to be able to pull in 2+ variables from the form and use them in the program. The guides i have seen for the beginner are all just one variable and i'm struggling to figure it out and could use some help.
Im looking for a second text field in the form and be able to get the variable in the app. I'm assuming it would be in the form of {'data': message['data']} and {'data': message['data2']} for instance but as long as I can get the values, thats whats important.
What I have right now:
index.html
<!DOCTYPE HTML>
<html>
<head>
<title>Flask-SocketIO Test</title>
<script type="text/javascript" src="//code.jquery.com/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/0.9.16/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
namespace = '/test'; // change to an empty string to use the global namespace
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
socket.on('connect', function() {
socket.emit('my event', {data: 'Connected... Waiting for you...'});
});
socket.on('my response', function(msg) {
$('#log').append('<br>' + msg.data);
});
$('form#emit').submit(function(event) {
socket.emit('my event', {data: $('#emit_data').val()});
return false;
});
});
</script>
</head>
<body>
<h1>Flask-SocketIO Test</h1>
<h2>Send:</h2>
<form id="emit" method='POST' action='#'>
<input type="text" name="emit_data" id="emit_data" placeholder="Message"><br>
<input type="submit" value="Echo"></div>
</form>
<h2>Receive:</h2>
<div id="log"></div>
</body>
</html>
app.py
from gevent import monkey
monkey.patch_all()
import time
from threading import Thread
from flask import Flask, render_template
from flask.ext.socketio import SocketIO, emit
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
thread = None
def background_thread():
"""Example of how to send server generated events to clients."""
count = 0
while True:
time.sleep(60)
count += 1
#'<br>Received #' + msg.count + ': ' + msg.data
socketio.emit('my response', {'data': 'Connection to server still alive'}, namespace='/test')
#app.route('/')
def index():
#kick off thread that every 10 seconds sends a response
global thread
if thread is None:
thread = Thread(target=background_thread)
thread.start()
return render_template('index.html')
#socketio.on('my event', namespace='/test')
def test_message(message):
print message
emit('my response', {'data': message['data']})
#socketio.on('connect', namespace='/test')
def test_connect():
emit('my response', {'data': 'Trying to connect to server...'})
#socketio.on('disconnect', namespace='/test')
def test_disconnect():
print('Client disconnected')
if __name__ == '__main__':
socketio.run(app)
Im looking for a second text field in the form and be able to get the variable in the app. I'm assuming it would be in the form of {'data': message['data']} and {'data': message['data2']} for instance but as long as I can get the values, thats whats important.
Just send as many variables as you want:
socket.emit('my event', {data: $('#emit_data').val(), data2: $('#emit_data2').val()});
data is only an example name for a variable. You can use any number and names of dictionary keys.

Categories

Resources