├── sensor-display-flask ├── .gitignore ├── requirements.txt ├── static │ ├── css │ │ └── app.css │ └── js │ │ └── app.js ├── sensor_app.py └── templates │ └── index.html ├── media ├── demo.gif ├── demo.mp4 ├── turbine.gif └── turbine.mp4 ├── Dockerfile ├── updater.py └── README.md /sensor-display-flask/.gitignore: -------------------------------------------------------------------------------- 1 | .venv -------------------------------------------------------------------------------- /sensor-display-flask/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | Flask-SocketIO==5.0.1 3 | Werkzeug==2.0.3 -------------------------------------------------------------------------------- /media/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Revisto/sensor-display/master/media/demo.gif -------------------------------------------------------------------------------- /media/demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Revisto/sensor-display/master/media/demo.mp4 -------------------------------------------------------------------------------- /media/turbine.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Revisto/sensor-display/master/media/turbine.gif -------------------------------------------------------------------------------- /media/turbine.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Revisto/sensor-display/master/media/turbine.mp4 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | COPY sensor-display-flask /app 4 | WORKDIR /app 5 | 6 | RUN pip3 install -U setuptools 7 | RUN apt-get install -y libssl-dev libffi-dev 8 | RUN apt-get install -y libxml2-dev libxslt1-dev zlib1g-dev 9 | RUN pip3 install -r requirements.txt 10 | 11 | CMD ["python3","sensor_app.py"] -------------------------------------------------------------------------------- /sensor-display-flask/static/css/app.css: -------------------------------------------------------------------------------- 1 | .hero { 2 | background: #eee; 3 | padding: 20px; 4 | border-radius: 10px; 5 | margin-top: 1em; 6 | } 7 | 8 | .hero h1 { 9 | margin-top: 0; 10 | margin-bottom: 0.3em; 11 | text-align: center; 12 | } 13 | 14 | 15 | .chart-container { 16 | max-width: 800px; 17 | margin: 0 auto; 18 | } 19 | 20 | .container { 21 | margin: auto; 22 | width: 49%; 23 | display: inline-block; 24 | } 25 | 26 | 27 | .killbtn { 28 | background-image: linear-gradient(to right, rgb(199 158 187) 0%, rgb(97 125 171) 100%) !important; 29 | width: -webkit-fill-available; 30 | height: 40px; 31 | } -------------------------------------------------------------------------------- /updater.py: -------------------------------------------------------------------------------- 1 | from random import randint, uniform 2 | import requests 3 | from time import sleep 4 | from os import getpid 5 | process_id = getpid() 6 | def sensor_data(): 7 | diesel_consumption = uniform(13, 14) 8 | tempreture = randint(480, 580) 9 | rpm = randint(2000, 4000) 10 | gas_mass = randint(250, 750) 11 | 12 | return { 13 | "diesel_consumption": diesel_consumption, 14 | "tempreture": tempreture, 15 | "rpm": rpm, 16 | "gas_mass": gas_mass, 17 | "process_id": process_id 18 | } 19 | 20 | while True: 21 | url = 'http://127.0.0.1:5000/update' 22 | data = sensor_data() 23 | requests.post(url, params = data) 24 | sleep(0.1) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sensor Tracker 2 | ## _Track and Display Turbine Sensors and Stop the Turbine!_ 3 | 4 | Sensor Tracker shows the 4 turbine sensors in different charts to be monitored and the agent would be able to stop the turbine if anything went wrong. 5 | 6 | ![](https://github.com/Revisto/sensor-display/blob/master/media/turbine.gif) 7 | 8 | 9 | ## ⚙️ Installation 10 | 11 | Sensor Tracker requires [Docker](https://www.docker.com/) to run. 12 | 13 | Run the updater.py and Install Docker and start the Sensor Tracker, docker takes care of other dependencies. 14 | 15 | ```sh 16 | apt install docker-ce 17 | ``` 18 | 19 | Now clone the repo: 20 | ```sh 21 | git clone https://github.com/revisto/sensor-display 22 | cd sensor-display 23 | ``` 24 | ![](https://github.com/Revisto/sensor-display/blob/master/media/demo.gif) 25 | 26 | ## Docker 27 | 28 | ```sh 29 | docker build -t sensor_tracker . 30 | docker run -d sensor_tracker 31 | ``` 32 | 33 | ## Updater 34 | 35 | ```sh 36 | python3 updater.py 37 | ``` 38 | 39 | ## Show your support 40 | 41 | Please ⭐️ this repository if this project helped you! 42 | 43 | 44 | ## 📝 License 45 | 46 | GNUv2 47 | 48 | **Free Software, Hell Yeah!** -------------------------------------------------------------------------------- /sensor-display-flask/sensor_app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, make_response, jsonify 2 | from flask_socketio import SocketIO 3 | from datetime import datetime 4 | import os 5 | app = Flask(__name__) 6 | app.config['SECRET_KEY'] = 'donsky!' 7 | socketio = SocketIO(app, cors_allowed_origins='*') 8 | class ProcessId: 9 | def __str__(self): 10 | return self.process_id 11 | def update(self, PID): 12 | self.process_id = PID 13 | 14 | process_id = ProcessId() 15 | 16 | """ 17 | Get current date time 18 | """ 19 | def get_current_datetime(): 20 | now = datetime.now() 21 | return now.strftime("%m/%d/%Y %H:%M:%S") 22 | 23 | """ 24 | Generate random sequence of dummy sensor values and send it to our clients 25 | """ 26 | @app.route('/update', methods = ['POST']) 27 | def update(): 28 | diesel_consumption = request.args.get('diesel_consumption') 29 | tempreture = request.args.get('tempreture') 30 | rpm = request.args.get('rpm') 31 | gas_mass = request.args.get('gas_mass') 32 | process_id.update(request.args.get('process_id')) 33 | 34 | socketio.emit('updateSensorData', {'diesel_consumption': diesel_consumption, "tempreture": tempreture, "rpm": rpm, "gas_mass": gas_mass, "date": get_current_datetime()}) 35 | data = {'message': 'Done', 'code': 'SUCCESS'} 36 | return make_response(jsonify(data), 201) 37 | 38 | @app.route('/kill') 39 | def kill(): 40 | os.system(f"kill {process_id}") 41 | return "SUCCESS" 42 | 43 | @app.route('/') 44 | def index(): 45 | return render_template('index.html') 46 | 47 | """ 48 | Decorator for connect 49 | """ 50 | @socketio.on('connect') 51 | def connect(): 52 | pass 53 | """ 54 | Decorator for disconnect 55 | """ 56 | @socketio.on('disconnect') 57 | def disconnect(): 58 | print('Client disconnected', request.sid) 59 | 60 | if __name__ == '__main__': 61 | socketio.run(app) -------------------------------------------------------------------------------- /sensor-display-flask/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Real Time Sensor Display Using Python, Flask, Flask-SocketIO 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | 28 |
29 |
30 |

Diesel consumption Sensor

31 |
32 | 33 |
34 |
35 |
36 |
37 |
38 |

Real Time Tempreture Display

39 |
40 | 41 |
42 |
43 |
44 |
45 |
46 |

Rounds Per Minute

47 |
48 | 49 |
50 |
51 |
52 |
53 |
54 |

Emission Gas Mass

55 |
56 | 57 |
58 |
59 |
60 | 61 | 67 | 68 | -------------------------------------------------------------------------------- /sensor-display-flask/static/js/app.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | const ctx = document.getElementById("myChart").getContext("2d"); 3 | 4 | const myChart = new Chart(ctx, { 5 | type: "line", 6 | data: { 7 | datasets: [{ }], 8 | }, 9 | options: { 10 | plugins: { 11 | legend: { 12 | display: false 13 | }, 14 | }, 15 | borderWidth: 3, 16 | borderColor: ['rgba(255, 99, 132, 1)',], 17 | }, 18 | }); 19 | myChart.options.animation = false; // disables all animations 20 | myChart.options.animations.colors = false; // disables animation defined by the collection of 'colors' properties 21 | myChart.options.animations.x = false; // disables animation defined by the 'x' property 22 | myChart.options.transitions.active.animation.duration = 0; // disables the animation for 'active' mode 23 | 24 | 25 | 26 | function addData(label, data) { 27 | myChart.data.labels.push(label); 28 | myChart.data.datasets.forEach((dataset) => { 29 | dataset.data.push(data); 30 | }); 31 | myChart.update(); 32 | } 33 | 34 | function removeFirstData() { 35 | myChart.data.labels.splice(0, 1); 36 | myChart.data.datasets.forEach((dataset) => { 37 | dataset.data.shift(); 38 | }); 39 | } 40 | 41 | const MAX_DATA_COUNT = 100; 42 | //connect to the socket server. 43 | // var socket = io.connect("http://" + document.domain + ":" + location.port); 44 | var socket = io.connect(); 45 | 46 | //receive details from server 47 | socket.on("updateSensorData", function (msg) { 48 | console.log("Received sensorData :: " + msg.date + " :: " + msg.diesel_consumption); 49 | 50 | // Show only MAX_DATA_COUNT data 51 | if (myChart.data.labels.length > MAX_DATA_COUNT) { 52 | removeFirstData(); 53 | } 54 | addData(msg.date, msg.diesel_consumption); 55 | }); 56 | 57 | 58 | 59 | const ctx2 = document.getElementById("myChart2").getContext("2d"); 60 | 61 | const myChart2 = new Chart(ctx2, { 62 | type: "line", 63 | data: { 64 | datasets: [{ }], 65 | }, 66 | options: { 67 | plugins: { 68 | legend: { 69 | display: false 70 | }, 71 | }, 72 | borderWidth: 3, 73 | borderColor: ['rgba(50, 50, 132, 1)',], 74 | }, 75 | }); 76 | 77 | myChart2.options.animation = false; // disables all animations 78 | myChart2.options.animations.colors = false; // disables animation defined by the collection of 'colors' properties 79 | myChart2.options.animations.x = false; // disables animation defined by the 'x' property 80 | myChart2.options.transitions.active.animation.duration = 0; // disables the animation for 'active' mode 81 | 82 | 83 | function addData2(label, data) { 84 | myChart2.data.labels.push(label); 85 | myChart2.data.datasets.forEach((dataset) => { 86 | dataset.data.push(data); 87 | }); 88 | myChart2.update(); 89 | } 90 | 91 | function removeFirstData2() { 92 | myChart2.data.labels.splice(0, 1); 93 | myChart2.data.datasets.forEach((dataset) => { 94 | dataset.data.shift(); 95 | }); 96 | } 97 | 98 | const MAX_DATA_COUNT2 = 100; 99 | //connect to the socket server. 100 | 101 | //receive details from server 102 | socket.on("updateSensorData", function (msg) { 103 | console.log("Received sensorData :: " + msg.date + " :: " + msg.tempreture); 104 | 105 | // Show only MAX_DATA_COUNT data 106 | if (myChart2.data.labels.length > MAX_DATA_COUNT2) { 107 | removeFirstData2(); 108 | } 109 | addData2(msg.date, msg.tempreture); 110 | }); 111 | 112 | const ctx3 = document.getElementById("myChart3").getContext("2d"); 113 | 114 | const myChart3 = new Chart(ctx3, { 115 | type: "line", 116 | data: { 117 | datasets: [{ }], 118 | }, 119 | options: { 120 | plugins: { 121 | legend: { 122 | display: false 123 | }, 124 | }, 125 | borderWidth: 3, 126 | borderColor: ['rgba(20, 200, 50, 1)',], 127 | }, 128 | }); 129 | 130 | myChart3.options.animation = false; // disables all animations 131 | myChart3.options.animations.colors = false; // disables animation defined by the collection of 'colors' properties 132 | myChart3.options.animations.x = false; // disables animation defined by the 'x' property 133 | myChart3.options.transitions.active.animation.duration = 0; // disables the animation for 'active' mode 134 | 135 | 136 | function addData3(label, data) { 137 | myChart3.data.labels.push(label); 138 | myChart3.data.datasets.forEach((dataset) => { 139 | dataset.data.push(data); 140 | }); 141 | myChart3.update(); 142 | } 143 | 144 | function removeFirstData3() { 145 | myChart3.data.labels.splice(0, 1); 146 | myChart3.data.datasets.forEach((dataset) => { 147 | dataset.data.shift(); 148 | }); 149 | } 150 | 151 | const MAX_DATA_COUNT3 = 100; 152 | //connect to the socket server. 153 | 154 | //receive details from server 155 | socket.on("updateSensorData", function (msg) { 156 | console.log("Received sensorData :: " + msg.date + " :: " + msg.rpm); 157 | 158 | // Show only MAX_DATA_COUNT data 159 | if (myChart3.data.labels.length > MAX_DATA_COUNT3) { 160 | removeFirstData3(); 161 | } 162 | addData3(msg.date, msg.rpm); 163 | }); 164 | const ctx4 = document.getElementById("myChart4").getContext("2d"); 165 | 166 | const myChart4 = new Chart(ctx4, { 167 | type: "line", 168 | data: { 169 | datasets: [{ }], 170 | }, 171 | options: { 172 | plugins: { 173 | legend: { 174 | display: false 175 | }, 176 | }, 177 | borderWidth: 3, 178 | borderColor: ['rgba(250, 200, 120, 1)',], 179 | }, 180 | }); 181 | 182 | myChart4.options.animation = false; // disables all animations 183 | myChart4.options.animations.colors = false; // disables animation defined by the collection of 'colors' properties 184 | myChart4.options.animations.x = false; // disables animation defined by the 'x' property 185 | myChart4.options.transitions.active.animation.duration = 0; // disables the animation for 'active' mode 186 | 187 | 188 | function addData4(label, data) { 189 | myChart4.data.labels.push(label); 190 | myChart4.data.datasets.forEach((dataset) => { 191 | dataset.data.push(data); 192 | }); 193 | myChart4.update(); 194 | } 195 | 196 | function removeFirstData4() { 197 | myChart4.data.labels.splice(0, 1); 198 | myChart4.data.datasets.forEach((dataset) => { 199 | dataset.data.shift(); 200 | }); 201 | } 202 | 203 | const MAX_DATA_COUNT4 = 100; 204 | //connect to the socket server. 205 | 206 | //receive details from server 207 | socket.on("updateSensorData", function (msg) { 208 | console.log("Received sensorData :: " + msg.date + " :: " + msg.gas_mass); 209 | 210 | // Show only MAX_DATA_COUNT data 211 | if (myChart4.data.labels.length > MAX_DATA_COUNT4) { 212 | removeFirstData4(); 213 | } 214 | addData4(msg.date, msg.gas_mass); 215 | }); 216 | }); 217 | --------------------------------------------------------------------------------