๐ ๊ณต๋ถํ๋ ์ง์ง์ํ์นด๋ ์ฒ์์ด์ง?
๋ค์ ๋์ .. Flask-SocketIO ์ฌ์ฉํด์ live-graph ๋ง๋ค๊ธฐ (8) ๋ณธ๋ฌธ
๋ค์ ๋์ .. Flask-SocketIO ์ฌ์ฉํด์ live-graph ๋ง๋ค๊ธฐ (8)
์ง์ง์ํ์นด 2022. 12. 2. 10:36<๋ณธ ๋ธ๋ก๊ทธ๋ donskytech ๋์ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํด์ ๊ณต๋ถํ๋ฉฐ ์์ฑํ์์ต๋๋ค :-)>
https://www.donskytech.com/python-flask-websockets/
Display Real-Time Updates Using Python, Flask, and Websockets
We will discuss how to display a real-time application using Python, Flask, and Websocket using the Flask-SocketIO library.
www.donskytech.com
๐ฅ Flask-SocketIO
Flask ์ ํ๋ฆฌ์ผ์ด์ ์ด ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ๋๊ธฐ ์๊ฐ์ด ์งง์ ์๋ฐฉํฅ ํต์ ์ ์ก์ธ์ค
ํด๋ผ์ด์ธํธ ์ธก ์ ํ๋ฆฌ์ผ์ด์ ์ Javascript, Python, C++, Java ๋ฐ Swift ์ ๋ชจ๋ SocketIO ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋๋ ๊ธฐํ ํธํ ๊ฐ๋ฅํ ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ์ ๋ํ ์๊ตฌ ์ฐ๊ฒฐ์ ์ค์
SocketIO
- ์ค์ ์ ์ก ํ๋กํ ์ฝ์์ ํด๋ผ์ด์ธํธ ์์ฉ ํ๋ก๊ทธ๋จ์ ์ถ์ํํ๋ ๋ธ๋ผ์ฐ์ ๊ฐ Javascript ๊ธฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
โ flask์์ socket ๋ง๋ค๊ธฐ
Flask-SocketIO ์ฐธ๊ณ
https://bokyeong-kim.github.io/python/flask/2020/05/09/flask(1).html
socketio = SocketIO(app)
- SocketIO๋ ‘์ฑ’์ ์ ์ฉ๋๊ณ ์์ผ๋ฉฐ ๋์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํ ๋ ์ฑ ๋์ socketio๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก socketio ๋ณ์์ ์ ์ฅ
- socketio๋ ์น ์๋ฒ๋ฅผ ์บก์ํ
@app.route('/')
def sessions():
return render_template('session.html')
- ํํ์ด์ง (๊ฒฝ๋ก ๋ฐ์ฝ๋ ์ดํฐ‘/’๋ก ํ์)๋ฅผ ๋ฐฉ๋ฌธํ๋ฉด ๊ทธ ์๋์ ์ ์ธ ๋ ์ธ์
๋ณด๊ธฐ๊ฐ ํธ๋ฆฌ๊ฑฐ
- render_template : HTML ํ์ด์ง๋ฅผ ๋ ๋๋ง, ํ ํ๋ฆฟ ํด๋์ ํฌํจ
- ์ธ๊ธ ๋ URL์ ์ด ๋ ํธ์ถ
โ Flask-Socket Handling
- ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ธ ๋ฉ์์ง๋ฅผ ๋ฐ๋ ๋ฐฉ๋ฒ
- ํด๋ผ์ด์ธํธ์ ๋ฉ์์ง๋ฅผ ํ์ธํ๋ ๋ฐฉ๋ฒ
- ํด๋ผ์ด์ธํธ์์ WebSocket ๋ฉ์์ง๋ฅผ ์์ ํ๊ธฐ ์ํด ์ ํ๋ฆฌ์ผ์ด์ ์ socketio.on ๋ฐ์ฝ๋ ์ดํฐ(@socketio.on())๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ ์
- send() ๋ฐ emit() ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ๊ฒฐ๋ ํด๋ผ์ด์ธํธ์ ์๋ต ๋ฉ์์ง๋ฅผ ๋ณด๋ผ ์ ์์
@socketio.on('my event')
# json ๊ฐ์ฒด ์์ -> myresponse ์ด๋ฒคํธ๋ก ์ ์ก
def handle_my_custom_event(json, methods=['GET', 'POST']):
print('received my event: ' + str(json))
# callback -> ์๋ฒ์ ์ํด ๋ฉ์์ง์ ์์ ์ฌ๋ถ ๋์์ฃผ๋ ๋๋ฒ๊น
socketio.emit('my response', json, callback=messageReceived)
โ ์ฐ๊ฒฐ ์ค์ ๋ฐ ์ธ์ ์์ฑ
var socket = io.connect('http://' + document.domain + ':' + location.port)
- io.connect()๋ฅผ ์ฌ์ฉ
- ๋ ์ฌ์ฉ์ ๋ชจ๋ ๋์ผํ URL์ ์ฐ๊ฒฐํ์ฌ ์ธ์ ์์ฑ
- document.domain์ ์์ ์ค์ธ ์ปดํจํฐ์ IP ์ฃผ์๋ฅผ ๋ํ๋ธ๋ค.
- location.port๋ ํฌํธ๋ฅผ ๋ํ๋ด๋ฉฐ, ๊ธฐ๋ณธ๊ฐ์ 5000
โ ์ด๋ฒคํธ
socket.on( 'connect', function() {
socket.emit( 'my event', {
data: 'User Connected'
})
var form = $( 'form' ).on( 'submit', function( e ) {
e.preventDefault()
let user_name = $( 'input.username' ).val()
let user_input = $( 'input.message' ).val()
socket.emit( 'my event', {
user_name : user_name,
message : user_input
})
$( 'input.message' ).val( '' ).focus()
})
})
- socket.on()์ ๋ํ ์ฒซ ๋ฒ์งธ ์ธ์๋ ์ด๋ฒคํธ ์ด๋ฆ
- ‘connect’, ‘disconnect’, ‘message’, ‘json’์ socketIO์ ์ํด ์์ฑ๋ ํน์ ์ด๋ฒคํธ
- ‘message’ : ํ์ ๋ฌธ์์ด์ ํ์ด๋ก๋(payload)๋ฅผ ์ ๋ฌ
- ‘json’ : json ์ฌ์ฉ์ ์ ์ ์ด๋ฒคํธ๋ ํ์ด์ฌ ์ฌ์ ์ ํํ๋ก JSON ํ์ด๋ก๋(payload)๋ฅผ ์ ๋ฌ
- ‘connect’, ‘disconnect’, ‘message’, ‘json’์ socketIO์ ์ํด ์์ฑ๋ ํน์ ์ด๋ฒคํธ
- send() : ๋ฌธ์์ด ๋๋ JSON ์ ํ์ ํ์ค ๋ฉ์์ง๋ฅผ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋
- emit() : ๋ฐ์ดํฐ์ ํจ๊ป ์ฌ์ฉ์ ์ ์ ์ด๋ฒคํธ ์ด๋ฆ(์ ์ฝ๋์์๋ ‘my event’)์ผ๋ก ๋ฉ์์ง๋ฅผ ์ ์ก
๐ฅ Chart.js
์ฐจํธ ์ ํ, ํ๋ฌ๊ทธ์ธ ๋ฐ ์ฌ์ฉ์ ์ ์ ์ต์ ์ธํธ๋ฅผ ์ ๊ณต
๋ด์ฅ TypeScript ํ์ดํ๊ณผ ํจ๊ป ์ ๊ณต๋๋ฉฐ React , Vue , Svelte ๋ฐ Angular ๋ฅผ ํฌํจํ์ฌ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๋ชจ๋ JavaScript ํ๋ ์์ํฌ ์ ํธํ
HTML5 ์บ๋ฒ์ค์์ ์ฐจํธ ์์๋ฅผ ๋ ๋๋ง
๐ฅ Web-Socket
๊ฑฐ์ ์ค์๊ฐ์ผ๋ก ์ ๋ณด๋ฅผ ํ์ํ๋ ์๋จ
์ค๋ฒํค๋๊ฐ ๊ฑฐ์ ์๋ ์๋ฐฉํฅ ๋ฐฉ์์ผ๋ก ์ ๋ณด๋ฅผ ์ ์กํ๋ ์๋จ
(HTTP ( Hypertext Transfer Protocol )๋ฅผ ์ฌ์ฉ ํ๋ฉด ์ ์ก ์๋ ์ธก๋ฉด์์ ์ฝ๊ฐ์ ์ค๋ฒํค๋๊ฐ ๋ฐ์)
Socket
- ํ ์ปดํจํฐ๊ฐ ๋ค๋ฅธ ์ปดํจํฐ์ ์ํธ ์์ฉํ ์ ์๋ ๊ฒฝ๋ก ์ค์
- ๊ฒ์ดํธ๊ฐ ์ด๋ ค์์ ๋, ์ฆ ์์ผ์ด ์ด๋ ค์๋ ๊ฒฝ์ฐ์๋ง ํต์ ์ ๊ฐ๋ฅํ๊ฒ ํ๋ ๊ฒ์ดํธ
๐ฅ Flask
์น ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ํ์ํด์ผ ํ๋ ๊ฒฝ์ฐ ์ฌ๋ฌผ ์ธํฐ๋ท(IOT) ์์ ์ฌ์ฉํ ์ ์๋ "๋ง์ดํฌ๋ก" ์น ํ๋ ์์ํฌ
HTML ํ์ด์ง ํ์ or JSON (Javascript Object Notation) ๋ฐํ
๐ฅ ์ฝ๋ ๊ตฌํ
- app.py
from flask import Flask, render_template, request
from flask_socketio import SocketIO
from random import random
from threading import Lock
from datetime import datetime
import pandas as pd
"""
Background Thread
"""
thread = None
thread_lock = Lock()
app = Flask(__name__)
app.config['SECRET_KEY'] = 'gani!'
socketio = SocketIO(app, cors_allowed_origins='*')
"""
Get current date time
"""
def get_current_datetime(years):
#now = datetime.now()
return years.strftime("%m-%d-%Y %H:%M:%S")
"""
Generate random sequence of dummy sensor values and send it to our clients
"""
CNT_WAIT = []
YEARS = []
def data_load() :
data = pd.read_csv('./data/test.csv')
for i in range(len(data)) :
CNT_WAIT.append(int(data["cnt_wait"][i]))
YEARS.append(data["insert_date_time"][i])
return CNT_WAIT, YEARS
def background_thread():
print("Generating random sensor values")
CNT_WAIT_data, YEARS_data = data_load()
#YEARS_data = get_current_datetime(YEARS_data)
cnt = 0
while True:
# ๋๋ค ์์๋ฅผ value : ์์ ๋ฃ์ผ๋ฉด ๋๋ค ๊ทธ๋ํ ์์ฑ
# dummy_sensor_value = round(random() * 100, 3)
print(CNT_WAIT_data[cnt])
print(YEARS_data[cnt])
# print(dummy_sensor_value)
socketio.emit('updateSensorData', {'value': CNT_WAIT_data[cnt], "date": YEARS_data[cnt]})
cnt += 1
socketio.sleep(1)
"""
Serve root index file
"""
@app.route('/')
def index():
return render_template('index.html')
"""
Decorator for connect
"""
@socketio.on('connect')
def connect():
global thread
print('Client connected')
global thread
with thread_lock:
if thread is None:
thread = socketio.start_background_task(background_thread)
"""
Decorator for disconnect
"""
@socketio.on('disconnect')
def disconnect():
print('Client disconnected', request.sid)
if __name__ == '__main__':
socketio.run(app)
- template/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Real Time Sensor Display Using Python, Flask, Flask-SocketIO</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.4/socket.io.js" integrity="sha512-aMGMvNYu8Ue4G+fHa359jcPb1u+ytAF+P2SCb+PxrjCdO3n3ZTxJ30zuH39rimUggmTwmh2u7wvQsDTHESnmfQ==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<link
href="//mincss.com/entireframework.min.css"
rel="stylesheet"
type="text/css"
/>
<link href="{{url_for('static', filename = 'css/app.css')}}" rel="stylesheet">
</head>
<body>
<script type="text/javascript" src="{{ url_for('static', filename = 'js/app.js') }}"></script>
<nav class="nav" tabindex="-1" onclick="this.focus()">
<div class="container">
<a class="pagename current" href="#">๋ฐฐ๊ณ ํ๋น๋น๊ตฌ๋ฆฌ</a>
</div>
</nav>
<button class="btn-close btn btn-sm">×</button>
<div class="container">
<div class="hero">
<h1>Real Time Graph Display</h1>
<div class="chart-container">
<canvas id="myChart" width="1000" height="600"></canvas>
</div>
</div>
</div>
</body>
</html>
- js/app.js
$(document).ready(function () {
const ctx = document.getElementById("myChart").getContext("2d");
const myChart = new Chart(ctx, {
type: "line",
data: {
datasets: [{ label: "Temperature", }],
},
options: {
borderWidth: 3,
borderColor: ['rgba(255, 99, 132, 1)',],
},
});
function addData(label, data) {
myChart.data.labels.push(label);
myChart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
myChart.update();
}
function removeFirstData() {
myChart.data.labels.splice(0, 1);
myChart.data.datasets.forEach((dataset) => {
dataset.data.shift();
});
}
const MAX_DATA_COUNT = 10;
//connect to the socket server.
// var socket = io.connect("http://" + document.domain + ":" + location.port);
var socket = io.connect();
//receive details from server
socket.on("updateSensorData", function (msg) {
console.log("Received sensorData :: " + msg.date + " :: " + msg.value);
// Show only MAX_DATA_COUNT data
if (myChart.data.labels.length > MAX_DATA_COUNT) {
removeFirstData();
}
addData(msg.date, msg.value);
});
});
- css/app.css
.hero {
background: #eee;
padding: 20px;
border-radius: 10px;
margin-top: 1em;
}
.hero h1 {
margin-top: 0;
margin-bottom: 0.3em;
text-align: center;
}
.chart-container {
max-width: 800px;
margin: 0 auto;
}
'๐ฉโ๐ป ๋ฐฑ์๋(Back-End) > Node js' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฐฑ์๋ express์ node.js ์ฌ์ฉํ๊ธฐ (2) (0) | 2023.01.20 |
---|---|
๋ฐฑ์๋ express์ node.js ์ฌ์ฉํ๊ธฐ (1) (0) | 2023.01.20 |
๋ค์ ๋์ .. Flask POST ์ฌ์ฉํด๋ณด๊ธฐ (7) (0) | 2022.12.01 |
๋ค์ ๋์ .. Flask GET ์ฌ์ฉํด๋ณด๊ธฐ (6) (0) | 2022.12.01 |
๋ค์ ๋์ .. Flask CSV ๋ถ๋ฌ์์ HTML์ ํ๋ก ์๊ฐํํ๊ธฐ(4) (0) | 2022.11.30 |