##// END OF EJS Templates
Fix power monitor
jespinoza -
r394:6790c8880a2c
parent child
Show More
1 NO CONTENT: new file 100644, binary diff hidden
@@ -1,160 +1,163
1 1 # docker-compose up -d --build
2 2 version: '3'
3 3
4 4 volumes:
5 5 sirm_web:
6 6 name: sirm_web
7 7 driver: local
8 8 driver_opts:
9 9 type: "none"
10 10 o: "bind"
11 11 device: "${EXPOSE_SIRM}"
12 12 sirm_pgdata:
13 13 name: sirm_pgdata
14 14 driver: local
15 15 driver_opts:
16 16 type: "none"
17 17 o: "bind"
18 18 device: "${EXPOSE_PGDATA}"
19 19 sirm_certs:
20 20 name: sirm_certs
21 21 driver: local
22 22 driver_opts:
23 23 type: "none"
24 24 o: "bind"
25 25 device: "${EXPOSE_CERTS}"
26 26 sirm_dhparam:
27 27 name: sirm_dhparam
28 28 driver: local
29 29 driver_opts:
30 30 type: "none"
31 31 o: "bind"
32 32 device: "${EXPOSE_DHPARAM}"
33 33 sirm_proc:
34 34 name: sirm_proc
35 35 driver: local
36 36 driver_opts:
37 37 type: "none"
38 38 o: "bind"
39 39 device: "${EXPOSE_PROC}"
40 40 sirm_nas:
41 41 name: sirm_nas
42 42 driver: local
43 43 driver_opts:
44 44 type: "none"
45 45 o: "bind"
46 46 device: "${EXPOSE_NAS}"
47 47
48 48 services:
49 49 sirm-nginx-proxy:
50 50 container_name: sirm-nginx-proxy
51 51 restart: always
52 52 build:
53 53 context: ./images/
54 54 dockerfile: nginx-proxy/Dockerfile
55 55 args:
56 56 - SIRM_MAX_UPLOAD_SIZE_MB=${SIRM_MAX_UPLOAD_SIZE_MB}
57 57 depends_on:
58 58 - sirm-web
59 59 networks:
60 60 - frontend_sirm
61 61 - backend_sirm
62 62 ports:
63 63 - 0.0.0.0:${GENERAL_PORT}:80
64 64 volumes:
65 65 - /var/run/docker.sock:/tmp/docker.sock:ro
66 66 - sirm_certs:/etc/nginx/certs:ro
67 67 - sirm_dhparam:/etc/nginx/dhparam
68 68 logging:
69 69 driver: "json-file"
70 70 options:
71 71 max-size: "12m"
72 72
73 73 sirm-web:
74 74 container_name: 'sirm-web'
75 75 restart: always
76 76 build:
77 77 context: .
78 78 environment:
79 79 - LC_ALL=${LC_ALL}
80 80 - DB_USER=${DB_USER}
81 81 - DB_NAME=${DB_NAME}
82 82 - DB_PASSWORD=${DB_PASSWORD}
83 83 - POSTGRES_PORT_5432_TCP_ADDR=${POSTGRES_PORT_5432_TCP_ADDR}
84 84 - POSTGRES_PORT_5432_TCP_PORT=${POSTGRES_PORT_5432_TCP_PORT}
85 85 - SIRM_USER=${SIRM_USER}
86 86 - SIRM_PASSWORD=${SIRM_PASSWORD}
87 87 - SIRM_EMAIL=${SIRM_EMAIL}
88 88 - VIRTUAL_HOST=${SIRM_SITE}
89 89 volumes:
90 90 - 'sirm_web:/workspace/sirm'
91 91 depends_on:
92 92 - sirm-postgres
93 93 networks:
94 94 - frontend_sirm
95 95 - backend_sirm
96 96 logging:
97 97 driver: "json-file"
98 98 options:
99 99 max-size: "12m"
100 100
101 101 sirm-postgres:
102 102 container_name: 'sirm-postgres'
103 103 restart: always
104 104 build:
105 105 context: ./images/
106 106 dockerfile: postgres/Dockerfile
107 107 args:
108 108 - PGDATA=${PGDATA}
109 109 environment:
110 110 - LC_ALL=${LC_ALL}
111 111 - DB_USER=${DB_USER}
112 112 - DB_NAME=${DB_NAME}
113 113 - DB_PASSWORD=${DB_PASSWORD}
114 114 - POSTGRES_PORT_5432_TCP_ADDR=${POSTGRES_PORT_5432_TCP_ADDR}
115 115 - POSTGRES_PORT_5432_TCP_PORT=${POSTGRES_PORT_5432_TCP_PORT}
116 116 volumes:
117 117 - sirm_pgdata:/var/lib/postgresql/data
118 118 networks:
119 119 - backend_sirm
120 120 logging:
121 121 driver: "json-file"
122 122 options:
123 123 max-size: "12m"
124 124
125 125 sirm-proc:
126 126 container_name: 'sirm-proc'
127 127 restart: always
128 128 build:
129 129 context: ./volumes/proc/
130 130 environment:
131 131 - BROKER_URL=${BROKER_URL}
132 - PEDESTAL_TOPIC=${PEDESTAL_TOPIC}
133 - TXA_SITE=${TXA_SITE}
134 - TXB_SITE=${TXB_SITE}
132 135 - VIRTUAL_HOST=${PROC_SITE}
133 136 volumes:
134 137 - 'sirm_proc:/app'
135 138 - 'sirm_nas:/data'
136 139 networks:
137 140 - frontend_sirm
138 141 logging:
139 142 driver: "json-file"
140 143 options:
141 144 max-size: "12m"
142 145
143 146 sirm-cam:
144 147 container_name: 'sirm-cam'
145 148 image: bkjaya1952/ivms4200-v2.8.2.2_ml-linux
146 149 restart: always
147 150 environment:
148 151 - VIRTUAL_HOST=${CAM_SITE}
149 152 networks:
150 153 - frontend_sirm
151 154 logging:
152 155 driver: "json-file"
153 156 options:
154 157 max-size: "12m"
155 158
156 159 networks:
157 160 frontend_sirm:
158 161 name: frontend_sirm
159 162 backend_sirm:
160 163 name: backend_sirm No newline at end of file
@@ -1,348 +1,396
1 1 """
2 2 A small Test application to show how to use Flask-MQTT.
3 3
4 4 """
5 5 import os
6 6 import time
7 7 import logging
8 8 from datetime import datetime
9 9 import random
10 10 import eventlet
11 11 import json
12 from json import JSONEncoder
12 13 import numpy
13 14 import base64
14 15 import h5py
15 16 try:
16 17 import requests
17 18 except:
18 19 pass
19 20 from bs4 import BeautifulSoup
20 21 from flask import Flask, render_template, jsonify, request, redirect
21 22 from flask_mqtt import Mqtt
22 23 from flask_socketio import SocketIO
23 24 from flask_bootstrap import Bootstrap
24 25
25 26 eventlet.monkey_patch()
26 27
27 28 app = Flask(__name__)
28 29 app.config['SECRET'] = 'my secret key'
29 30 app.config['TEMPLATES_AUTO_RELOAD'] = True
30 31 app.config['MQTT_BROKER_URL'] = os.environ['BROKER_URL']
31 32 app.config['MQTT_BROKER_PORT'] = 1883
32 33 app.config['MQTT_CLIENT_ID'] = 'flask_mqtt_sophy'
33 34 app.config['MQTT_CLEAN_SESSION'] = True
34 35 app.config['MQTT_USERNAME'] = ''
35 36 app.config['MQTT_PASSWORD'] = ''
36 37 app.config['MQTT_KEEPALIVE'] = 5
37 38 app.config['MQTT_TLS_ENABLED'] = False
38 39 app.config['MQTT_LAST_WILL_TOPIC'] = 'home/lastwill'
39 40 app.config['MQTT_LAST_WILL_MESSAGE'] = 'bye'
40 41 app.config['MQTT_LAST_WILL_QOS'] = 2
41 42
42 43 try:
43 44 mqtt = Mqtt(app)
44 45 except:
45 46 pass
47
46 48 socketio = SocketIO(app)
47 49 bootstrap = Bootstrap(app)
48 50
49 51 MODE_STOP = 0x00
50 52 MODE_SPEED = 0x01
51 53 MODE_POSITION = 0x02
52 54 RX_AZIMUTH = 0x27
53 55 RX_ELEVATION = 0x47
54 56 TX_AZIMUTH = 0x25
55 57 TX_ELEVATION = 0x45
56 58 NONE_AXIS = 0x65
57 59 RX_FUNCTION = 0x30
58 60 TX_FUNCTION = 0x40
59 61 HEADER = 0x7e
60 62 MIN_SPEED = -180.0
61 63 MAX_SPEED = 180.0
62 64 SHRT_MIN = -32768
63 65 SHRT_MAX = 32768
64 66 USHRT_MAX = 65535
65 67
66 68 DATA_PATH = '/data'
67 69 RUNNING = False
68 70 EXPERIMENT = {'name': 'test'}
71 DEBUG = False
72
73 class NumpyArrayEncoder(JSONEncoder):
74 def default(self, obj):
75 if isinstance(obj, numpy.ndarray):
76 return obj.tolist()
77 return JSONEncoder.default(self, obj)
69 78
70 79 class HDF5File():
71 80
72 81 def __init__(self):
73 82 self.data = {}
83 self.data_pow = {'powa':[], 'powb':[], 'timestamp':[]}
84 self.power_ready = True
85 self.power_time = 0
74 86 self.raw = {}
75 87 self.timestamp = 0
76 88 self.enable = False
77 89 self.metadata = None
78 90
79 def ready(self):
80
81 if len(self.az)<100 or len(self.el)<100 or len(self.saz)<100 or len(self.sel)<100:
82 return False
83 return True
91 def reset(self):
92 self.data = {}
93 self.data_pow = {'powa':[], 'powb':[], 'timestamp':[]}
94 self.power_ready = True
95 self.power_time = 0
96 self.raw = {}
97 self.timestamp = 0
84 98
85 99 def add_data(self, var, values):
86 100
87 101 if len(values) > 0:
88 102 delta = numpy.average(values[1:]-values[:-1])
89 103 n = 100 - len(values)
90 # print(values[-1]+delta, values[0], delta, flush=True)
91 # missing = numpy.arange(values[-1]+delta, values[0], delta)
92 print(var, n, numpy.arange(1, n+1)*delta + values[-1], flush=True)
93 104 if n > 0:
94 105 missing = numpy.arange(1, n+1)*delta + values[-1]
95 106 values = values.tolist()
96 107 values.extend(missing)
97 108
98 109 self.data[var] = values
99 110
100 111 def update(self, data):
101 112
102 113 self.add_data('az', data['az'])
103 114 self.add_data('el', data['el'])
104 115 self.add_data('saz', data['saz'])
105 116 self.add_data('sel', data['sel'])
106 117 self.timestamp = int(data['timestamp'])
107 118
108 119 def create_file(self):
109 120
110 121 filex = "pos@%10.3f.h5" % (self.timestamp)
111 122 date_folder = datetime.fromtimestamp(self.timestamp).strftime('%Y-%m-%dT%H-00-00')
112 123 filename = os.path.join(DATA_PATH, EXPERIMENT['name'], 'position', date_folder, filex)
113 124 if not os.path.exists(os.path.dirname(filename)):
114 125 path = os.path.dirname(filename)
115 126 os.makedirs(path)
116 127 with h5py.File(filename, 'w') as fp:
117 128 grp = fp.create_group("Data")
118 129 dset = grp.create_dataset("azi_pos", data=numpy.array(self.data['az']))
119 130 dset = grp.create_dataset("ele_pos", data=numpy.array(self.data['el']))
120 131 dset = grp.create_dataset("azi_speed", data=numpy.array(self.data['saz']))
121 132 dset = grp.create_dataset("ele_speed", data=numpy.array(self.data['sel']))
122 133 dset = grp.create_dataset("utc", data=self.timestamp)
123 filelog = filename.replace('.h5', '.txt')
124 f = open(filelog, 'w')
125 f.write(json.dumps(self.raw))
126 f.close()
134 if DEBUG:
135 filelog = filename.replace('.h5', '.txt')
136 f = open(filelog, 'w')
137 f.write(json.dumps(self.raw, cls=NumpyArrayEncoder))
138 f.close()
127 139
128 140 def write(self, data):
129 141
130 142 self.raw = data
131 143 self.update(data)
132 144 self.create_file()
133 145
146 def write_power(self, data):
147
148 if self.power_ready:
149 filex = "pow@%10.3f.h5" % (self.power_time if self.power_time else self.timestamp)
150 date_folder = datetime.fromtimestamp(self.timestamp).strftime('%Y-%m-%dT%H-00-00')
151 filename = os.path.join(DATA_PATH, EXPERIMENT['name'], 'power', date_folder, filex)
152 if not os.path.exists(os.path.dirname(filename)):
153 path = os.path.dirname(filename)
154 os.makedirs(path)
155 self.fp = h5py.File(filename, 'w')
156 self.power_ready = False
157
158 if datetime.fromtimestamp(self.timestamp).second == 0:
159 self.power_time = self.timestamp
160 grp = self.fp.create_group("Data")
161 dset = grp.create_dataset("powa", data=numpy.array(self.data_pow['powa']))
162 dset = grp.create_dataset("powb", data=numpy.array(self.data_pow['powb']))
163 dset = grp.create_dataset("utc", data=numpy.array(self.data_pow['timestamp']))
164 self.fp.close()
165 self.data_pow['powa'] = []
166 self.data_pow['powb'] = []
167 self.data_pow['timestamp'] = []
168 self.power_ready = True
169
170 self.data_pow['powa'].append(data['powa'])
171 self.data_pow['powb'].append(data['powb'])
172 self.data_pow['timestamp'].append(self.timestamp)
173
174
134 175 HDF = HDF5File()
135 176
136 177 def getSpeedPosition(msg_b64):
137 178 AzPosition=[]
138 179 AzSpeed=[]
139 180 ElPosition=[]
140 181 ElSpeed=[]
141 182 RawData=[]
142 183 raw = numpy.frombuffer(base64.decodebytes(msg_b64.encode()),numpy.dtype('B'))
143 184 raw_size = len(raw)
144 185 Timestamp = (raw[raw_size-4])|(raw[raw_size-3]<<8)|(raw[raw_size-2]<<16)|(raw[raw_size-1]<<24)
145 186 # Timestamp = time.time()
146 187 counter = 0
147 188 while counter < raw_size-4:
148 189 if raw[counter]==HEADER:
149 190 if raw[counter+1]==RX_FUNCTION:
150 191 if raw[counter+2]==RX_AZIMUTH or raw[counter+2]==RX_ELEVATION:
151 192 iangle = 0.0
152 193 ispeed = 0.0
153 194 hadstuffing = 0
154 195
155 196 if (counter+hadstuffing+4<raw_size-4):
156 197 if raw[counter+hadstuffing+4]==HEADER-1:
157 198 hadstuffing+=1
158 199 iangle = int(0x70|raw[counter+hadstuffing+4]&0x0F)
159 200 else:
160 201 iangle = int(raw[counter+hadstuffing+4])
161 202 else:
162 203 logging.debug("Warning: Index out of bounds. The packet is incomplete or corrupted.")
163 204 break
164 205
165 206 if (counter+hadstuffing+5<raw_size-4):
166 207 if raw[counter+hadstuffing+5]==HEADER-1:
167 208 hadstuffing+=1
168 209 iangle = iangle + int((0x70|raw[counter+hadstuffing+5]&0x0F)<<8)
169 210 else:
170 211 iangle = iangle + int(raw[counter+hadstuffing+5]<<8)
171 212 else:
172 213 logging.debug("Warning: Index out of bounds. The packet is incomplete or corrupted.")
173 214 break
174 215
175 216 if (counter+hadstuffing+6<raw_size-4):
176 217 if raw[counter+hadstuffing+6]==HEADER-1:
177 218 hadstuffing+=1
178 219 ispeed = int(0x70|raw[counter+hadstuffing+6]&0x0F)
179 220 else:
180 221 ispeed = int(raw[counter+hadstuffing+6])
181 222 else:
182 223 logging.debug("Warning: Index out of bounds. The packet is incomplete or corrupted.")
183 224 break
184 225
185 226 if (counter+hadstuffing+7<raw_size-4):
186 227 if raw[counter+hadstuffing+7]==HEADER-1:
187 228 hadstuffing+=1
188 229 ispeed = ispeed + int((0x70|raw[counter+hadstuffing+7]&0x0F)<<8)
189 230 else:
190 231 ispeed = ispeed + int(raw[counter+hadstuffing+7]<<8)
191 232 else:
192 233 logging.debug("Warning: Index out of bounds. The packet is incomplete or corrupted.")
193 234 break
194 235
195 236 if (counter+2<raw_size-4):
196 237 if raw[counter+2]==RX_AZIMUTH:
197 238 AzPosition.append(iangle*360.0/USHRT_MAX)
198 239 AzSpeed.append(((ispeed-SHRT_MIN)*(MAX_SPEED-MIN_SPEED)/(SHRT_MAX-SHRT_MIN))+MIN_SPEED)
199 240 elif raw[counter+2]==RX_ELEVATION:
200 241 ElPosition.append(iangle*360.0/USHRT_MAX)
201 242 ElSpeed.append(((ispeed-SHRT_MIN)*(MAX_SPEED-MIN_SPEED)/(SHRT_MAX-SHRT_MIN))+MIN_SPEED)
202 243 else:
203 244 counter+=1
204 245 continue
205 246 else:
206 247 logging.debug("Warning: Index out of bounds. The packet is incomplete or corrupted.")
207 248 break
208 249
209 250 counter = counter+hadstuffing+13
210 251 else:
211 252 counter+=1
212 253 continue
213 254 else:
214 255 counter+=1
215 256 continue
216 257 else:
217 258 counter+=1
218 259 continue
219 260
220 261 az = numpy.array(AzPosition)
221 262 az[numpy.where(az>=359.5)] = 0
222 263
223 264 saz = numpy.array(AzSpeed)
224 265 saz[numpy.where(saz>=28)] = saz[(saz>=28)]-360
225 266
226 267 el = numpy.array(ElPosition)
227 268 el[numpy.where((190 <= el) & (el <= 360))] = el[(190 <= el) & (el<= 360)] - 360
228 269
229 270 sel = numpy.array(ElSpeed)
230 271 sel[numpy.where(sel>=28)] = sel[(sel>=28)]-360
231 272
232 273 return az, saz, el, sel, int(Timestamp)
233 274
234 275 def get_power(tx):
235 276
236 277 url = 'http://{}/status.xml'.format(tx)
237 278 try:
238 279 page = requests.get(url, timeout=0.2)
239 280 soup = BeautifulSoup(page.content, 'xml')
240 281 power = float(soup.find('bucAOutputpower').text)
241 282 except:
242 283 power = 0.0
243 284 return power
244 285
245 286
246 287 @app.route('/')
247 288 def index():
248 289
249 290 return render_template('index.html')
250 291
251 292 @app.route('/start', methods=['POST'])
252 293 def start_proc():
253 294
254 295 global EXPERIMENT, RUNNING
255 296
256 297 EXPERIMENT.update(request.get_json())
257 298 RUNNING = True
258 299 print(EXPERIMENT, flush=True)
259 300 path = os.path.join(DATA_PATH, EXPERIMENT['name'])
260 301 if not os.path.exists(path):
261 302 os.makedirs(path)
262 303 fo = open(os.path.join(path, 'experiment.conf'), 'w')
263 304 fo.write(json.dumps(EXPERIMENT, indent=2))
264 305 fo.close()
265 306
266 307 return jsonify({'start': 'ok'})
267 308
268 309 @app.route('/stop')
269 310 def stop_proc():
270 311
271 global RUNNING
312 global RUNNING, DEBUG, HDF
313
272 314 RUNNING = False
315 DEBUG = False
316 HDF.reset()
273 317
274 318 return jsonify({'stop': 'ok'})
275 319
276 320 @app.route('/status')
277 321 def status_proc():
278 322
279 323 global RUNNING
280 324
281 325 return jsonify({'status': RUNNING})
282 326
283 327 @app.route('/run')
284 328 def run_proc():
285 329
286 global RUNNING, EXPERIMENT
330 global RUNNING, EXPERIMENT, DEBUG
331 if request.args.get('debug', False):
332 DEBUG = True
287 333 path = os.path.join(DATA_PATH, 'TEST')
288 334 if not os.path.exists(path):
289 335 os.makedirs(path)
290 336 EXPERIMENT['name'] = 'TEST'
291 337 RUNNING = True
292 338
293 339 return redirect('/')
294 340
295 341 @socketio.on('publish')
296 342 def handle_publish(json_str):
297 343 data = json.loads(json_str)
298 344 mqtt.publish(data['topic'], data['message'], data['qos'])
299 345
300 346 @socketio.on('subscribe')
301 347 def handle_subscribe(json_str):
302 348 data = json.loads(json_str)
303 349 mqtt.subscribe(data['topic'], data['qos'])
304 350
305 351 @socketio.on('unsubscribe_all')
306 352 def handle_unsubscribe_all():
307 353 mqtt.unsubscribe_all()
308 354
309 355 @mqtt.on_connect()
310 356 def handle_connect(client, userdata, flags, rc):
311 357 mqtt.subscribe(os.environ.get('PEDESTAL_TOPIC', 'JRO_topic'))
312 358
313 359 @mqtt.on_message()
314 360 def handle_mqtt_message(client, userdata, message):
315 361
316 global RUNNING
362 global RUNNING, HDF
317 363
318 364 payload = message.payload.decode()
319 365 az, saz, el, sel, tm = getSpeedPosition(payload)
320 pwA = get_power(os.environ['TXA_SITE'])
321 pwB = get_power(os.environ['TXB_SITE'])
322 if RUNNING:
323 HDF.write({'az':az, 'el':el, 'saz':saz, 'sel':sel, 'timestamp':tm})
366 powa = get_power(os.environ['TXA_SITE'])
367 powb = get_power(os.environ['TXB_SITE'])
324 368 times = numpy.arange(tm, tm+1, 0.1, dtype=float)
325 369
326 370 data = dict(
327 371 az =az[::10].tolist(),
328 372 el =el[::10].tolist(),
329 373 saz =saz[::10].tolist(),
330 374 sel =sel[::10].tolist(),
331 375 topic = message.topic,
332 376 payload = payload,
333 377 qos = message.qos,
334 378 timestamp = times.tolist(),
335 pwa = [pwa],
336 pwb = [pwb],
379 powa = [powa],
380 powb = [powb],
337 381 status = 'Running' if RUNNING else 'Not Running'
338 382 )
339 383
340 384 socketio.emit('mqtt_message', data=data)
341 385
386 if RUNNING:
387 HDF.write({'az':az, 'el':el, 'saz':saz, 'sel':sel, 'timestamp':tm, 'powa':powa, 'powb':powb})
388 HDF.write_power({'timestamp':tm, 'powa':powa, 'powb':powb})
389
342 390 @mqtt.on_log()
343 391 def handle_logging(client, userdata, level, buf):
344 392 # print(level, buf)
345 393 pass
346 394
347 395 if __name__ == '__main__':
348 396 socketio.run(app, host='0.0.0.0', port=5000, use_reloader=False, debug=True)
@@ -1,194 +1,181
1 1 {% extends "bootstrap/base.html" %}
2 2 {% block title %}SOPHY monitor{% endblock %}
3 3
4 4 {% block styles %}
5 5 {{ super() }}
6 6 {% endblock %}
7 7
8 8 {% block scripts %}
9 9 {{ super() }}
10 10 <script type="text/javascript" src="{{ url_for('static', filename='js/plotly-latest.min.js') }}"></script>
11 11 <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/4.4.1/socket.io.min.js"></script>
12 12 <script type="text/javascript" charset="utf-8">
13 13 $(document).ready(function() {
14 14 var socket = io.connect('http://' + document.domain + ':' + location.port);
15 15
16 16 socket.on('connect', function(data) {
17 17 console.log('Initialazing plots...');
18 makePlot("plot-az");
19 makePlot("plot-el");
20 makePlot("plot-saz");
21 makePlot("plot-sel");
22 makePlot("plot-tx");
23
18 makePlot("plot-pos", 2, ["Azimuth", "Elevation"]);
19 makePlot("plot-speed", 2, ["AZ Speed", "EL Speed"]);
20 makePlot("plot-tx", 2, ["Power A", "Power B"]);
24 21 })
25 22
26 23 socket.on('mqtt_message', function(data) {
27 24 console.log(data.topic);
28 25 var text = '(' + data['topic'] + ' qos: ' + data['qos'] + ') ' + data['payload'];
29 26 $('#subscribe_messages').val(text);
30 27 $('#radar_status').val(data.status);
31 streamPlot("plot-az", data.timestamp, data.az );
32 streamPlot("plot-el", data.timestamp, data.el );
33 streamPlot("plot-saz", data.timestamp, data.saz );
34 streamPlot("plot-sel", data.timestamp, data.sel );
28 streamPlot2("plot-pos", data.timestamp, data.az, data.el );
29 streamPlot2("plot-speed", data.timestamp, data.saz, data.sel );
30 streamPlot2("plot-tx", data.timestamp, data.powa, data.powb );
35 31 })
36 32 });
37 33
38 function makePlot(div){
34 function makePlot(div, n=1, names=["", ""]){
39 35 var plotDiv = document.getElementById(div);
40 var traces = [{
41 x: [],
42 y: []
43 }];
36 var traces = [];
37 for (let i = 0; i < n; i++) {
38 traces.push({x: [], y: [], name: names[i]});
39 }
44 40 var layout = {
45 41 height: 350,
46 42 font: {size: 12},
47 43 margin: { t: 0 },
48 44 xaxis: {
49 45 type: 'date'
50 46 },
51 //yaxis: {
52 // range: [0, 110]
53 //}
54 47 };
55 48
56 49 Plotly.plot(plotDiv, traces, layout);
57 50 };
58 51
59 52 function streamPlot(div, x, y ){
60 53 var plotDiv = document.getElementById(div);
61 54 if (plotDiv.data[0].x.length > 499){
62 55 plotDiv.data[0].x = plotDiv.data[0].x.slice(-500)
63 56 plotDiv.data[0].y = plotDiv.data[0].y.slice(-500)
64 57 }
65 58 var tm = x.map(i => new Date(i*1000));
66 59 var values = y.map(i => Math.round(i * 100) / 100);
67 60 var data_update = {x: [tm], y: [values]}
68 //var windowDateObj = getWindow(x)
69 //var layout_update = {xaxis: {
70 // range: [windowDateObj, x[x.length - 1]],
71 // rangeslider: {range: [plot_start, x[x.length - 1]]}
72 //}};
73 //Plotly.update(plotDiv, {}, layout_update)
74 61 Plotly.extendTraces(plotDiv, data_update, [0])
75 };
62 };
63
64 function streamPlot2(div, x, y1, y2 ){
65 var plotDiv = document.getElementById(div);
66 if (plotDiv.data[0].x.length > 499){
67 plotDiv.data[0].x = plotDiv.data[0].x.slice(-500)
68 plotDiv.data[0].y = plotDiv.data[0].y.slice(-500)
69 plotDiv.data[1].x = plotDiv.data[1].x.slice(-500)
70 plotDiv.data[1].y = plotDiv.data[1].y.slice(-500)
71 }
72 var tm = x.map(i => new Date(i*1000));
73 var values1 = y1.map(i => Math.round(i * 100) / 100);
74 var values2 = y2.map(i => Math.round(i * 100) / 100);
75 var data_update = {x: [tm, tm], y: [values1, values2]}
76 Plotly.extendTraces(plotDiv, data_update, [0, 1])
77 };
76 78
77 79 </script>
78 80 {% endblock %}
79 81
80 82 {% block content %}
81 83 <div class="container-fluid">
82 84 <div class="row">
83 85 <div class="col-xs-12">
84 86 <h1>SOPHy monitor</h1>
85 87 </div>
86 88 </div>
87 89 <div class="row">
88 90 <!--Messages-->
89 91 <div class="col-xs-6">
90 92 <div class="panel panel-default">
91 93 <div class="panel-heading">
92 94 <h3 class="panel-title">Status</h3>
93 95 </div>
94 96 <div class="panel-body">
95 97 <div class="col-xs-12">
96 98 <div class="row">
97 99 <div class="form-horizontal">
98 100 <div class="form-group">
99 101 <div class="col-xs-12">
100 102 <label for="radar_status">Status</label>
101 103 <input id="radar_status" class="form-control" value="Not Running", disabled></input>
102 104 </div>
103 105 </div>
104 106 <div class="form-group">
105 107 <div class="col-xs-12">
106 108 <label for="subscribe_messages">MQTT Message</label>
107 109 <textarea id="subscribe_messages" class="form-control" rows=8></textarea>
108 110 </div>
109 111 </div>
110 112 </div>
111 113 </div>`
112 114 </div>
113 115 </div>
114 116 </div>
115 117 </div>
116 <!--Azimuth-->
117 <div class="col-xs-6">
118 <div class="panel panel-default">
119 <div class="panel-heading">
120 <h3 class="panel-title">Pedestal Azimuth</h3>
121 </div>
122 <div class="panel-body">
123 <div class="col-xs-12">
124 <div class="row">
125 <div id="plot-az"></div>
126 </div>
127 </div>
128 </div>
129 </div>
130 </div>
131 <!--Elevation-->
118 <!--RX LOG-->
132 119 <div class="col-xs-6">
133 120 <div class="panel panel-default">
134 121 <div class="panel-heading">
135 <h3 class="panel-title">Pedestal Elevation</h3>
122 <h3 class="panel-title">RX log</h3>
136 123 </div>
137 124 <div class="panel-body">
138 125 <div class="col-xs-12">
139 126 <div class="row">
140 <div id="plot-el"></div>
127 <div></div>
141 128 </div>
142 129 </div>
143 130 </div>
144 131 </div>
145 132 </div>
146 <!--Azimuth Speed-->
133 <!--Position-->
147 134 <div class="col-xs-6">
148 135 <div class="panel panel-default">
149 136 <div class="panel-heading">
150 <h3 class="panel-title">Pedestal Azimuth Speed</h3>
137 <h3 class="panel-title">Pedestal Position</h3>
151 138 </div>
152 139 <div class="panel-body">
153 140 <div class="col-xs-12">
154 141 <div class="row">
155 <div id="plot-saz"></div>
142 <div id="plot-pos"></div>
156 143 </div>
157 144 </div>
158 145 </div>
159 146 </div>
160 147 </div>
161 <!--Elevation Speed-->
148 <!--Speed-->
162 149 <div class="col-xs-6">
163 150 <div class="panel panel-default">
164 151 <div class="panel-heading">
165 <h3 class="panel-title">Pedestal Elevation Speed</h3>
152 <h3 class="panel-title">Pedestal Speed</h3>
166 153 </div>
167 154 <div class="panel-body">
168 155 <div class="col-xs-12">
169 156 <div class="row">
170 <div id="plot-sel"></div>
157 <div id="plot-speed"></div>
171 158 </div>
172 159 </div>
173 160 </div>
174 161 </div>
175 162 </div>
176 163 <!--TX power-->
177 164 <div class="col-xs-6">
178 165 <div class="panel panel-default">
179 166 <div class="panel-heading">
180 167 <h3 class="panel-title">SSPA's power</h3>
181 168 </div>
182 169 <div class="panel-body">
183 170 <div class="col-xs-12">
184 171 <div class="row">
185 172 <div id="plot-tx"></div>
186 173 </div>
187 174 </div>
188 175 </div>
189 176 </div>
190 177 </div>
191 178 </div>
192 179 </div>
193 180 {{debug}}
194 181 {% endblock %} No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now