##// END OF EJS Templates
Test Version
Juan C. Espinoza -
r22:3d86891def25
parent child
Show More
@@ -0,0 +1,9
1 from django.conf.urls import url
2
3 from . import consumers
4
5 websocket_urlpatterns = [
6 url(r'^ws/main/$', consumers.MainConsumer),
7 url(r'^ws/realtime/(?P<code>[^/]+)/(?P<plot>[^/]+)/$', consumers.PlotConsumer),
8 url(r'^ws/database/(?P<code>[^/]+)/(?P<plot>[^/]+)/$', consumers.PlotConsumer),
9 ] No newline at end of file
@@ -1,16 +1,16
1 FROM python:2.7-slim
1 FROM python:3-slim
2 2 RUN mkdir /app
3 3 WORKDIR /app
4 4 ADD requirements.txt ./requirements.txt
5 5 RUN apt-get clean && apt-get update && apt-get install -y --no-install-recommends \
6 6 g++ \
7 7 gcc \
8 8 tzdata \
9 9 && ln -snf /usr/share/zoneinfo/America/Lima /etc/localtime \
10 10 && echo "America/Lima" > /etc/timezone \
11 11 && dpkg-reconfigure -f noninteractive tzdata \
12 12 && pip install -r requirements.txt \
13 13 && apt-get purge -y --auto-remove gcc g++ \
14 14 && rm -rf /var/lib/apt/lists/*
15 15 COPY . /app/
16 16 No newline at end of file
@@ -1,57 +1,57
1 1 version: '2'
2 2
3 3 services:
4 4 web:
5 5 container_name: 'realtime'
6 6 build: .
7 7 restart: always
8 8 image: realtime
9 9 command: python manage.py runserver 0.0.0.0:8000
10 10 env_file: .env
11 11 ports:
12 12 - "8000:8000"
13 13 links:
14 14 - redis
15 15 - mongo
16 16 volumes:
17 17 - './:${APP_DIR}'
18 18 depends_on:
19 19 - redis
20 20 - mongo
21 21
22 22 zmq_server:
23 23 container_name: 'realtime_zmq'
24 24 restart: always
25 25 image: 'realtime'
26 26 ports:
27 27 - '4444:4444'
28 28 command: 'python -u scripts/server.py'
29 29 env_file: .env
30 30 links:
31 31 - redis
32 32 - mongo
33 33 volumes:
34 34 - './:${APP_DIR}'
35 35 depends_on:
36 36 - web
37 37
38 38 redis:
39 39 container_name: 'realtime_redis'
40 40 image: 'redis:3.2-alpine'
41 41 ports:
42 42 - '127.0.0.1:6379:6379'
43 43 volumes:
44 44 - 'redisdata:/data'
45 45
46 46 mongo:
47 47 container_name: 'realtime_mongo'
48 image: 'mongo:3.3'
48 image: 'mongo:4.0'
49 49 command: '--storageEngine wiredTiger'
50 50 ports:
51 51 - '127.0.0.1:27017:27017'
52 52 volumes:
53 53 - 'mongodata:/data/db'
54 54
55 55 volumes:
56 56 redisdata:
57 57 mongodata: No newline at end of file
@@ -1,81 +1,172
1 1 #!/usr/bin/python
2 2 # -*- coding: UTF-8 -*-
3 3 import os
4 4 import json
5 5
6 6 from datetime import datetime, timedelta
7 7
8 8 from pymongo import MongoClient
9 import mongoengine
10 from asgiref.sync import async_to_sync
11 from channels.generic.websocket import WebsocketConsumer
12
13 from plotter.models import Experiment, ExpDetail, PlotMeta, PlotData
9 14
10 from channels.handler import AsgiHandler
11 from channels.auth import channel_session_user
12 from channels import Group
13 15 # Here we create the db named "dbplots"
14 16 host = os.environ.get('HOST_MONGO', 'localhost')
15 CLIENT = MongoClient('{}:27017'.format(host))
16 DB = CLIENT['dbplots']
17
18 # Connected to websocket.connect
19 def ws_connect(message, id, code, plot):
20
21 if id == 'main':
22 Group('main').add(message.reply_channel)
23 print('New main connection')
24 elif id == 'realtime':
25 Group('{}_{}'.format(code, plot)).add(message.reply_channel)
26 print('New connection from: {}, Group: {}_{}'.format(message.content['client'][0], code, plot))
17 mongoengine.connect('dbplots', host=host, port=27017)
18
19 # CLIENT = MongoClient('{}:27017'.format(host))
20 # DB = CLIENT['dbplots']
21
22
23 class MainConsumer(WebsocketConsumer):
24
25 def connect(self):
26 self.group_name = 'main'
27 async_to_sync(self.channel_layer.group_add)(
28 self.group_name,
29 self.channel_name
30 )
31 self.accept()
32
33 def disconnect(self, close_code):
34 async_to_sync(self.channel_layer.group_discard)(
35 self.group_name,
36 self.channel_name
37 )
38
39 def receive(self, text_data):
40 pass
41
42 def zmq_message(self, event):
43 # Send message to WebSocket
44 self.send(text_data=event['message'])
45
46 class PlotConsumer(WebsocketConsumer):
47
48 def connect(self):
49
50 if 'realtime' in self.scope['path']:
51 self.realtime = True
52 self.group_name = '{}_{}'.format(
53 self.scope['url_route']['kwargs']['code'],
54 self.scope['url_route']['kwargs']['plot'],
55 )
56
57 async_to_sync(self.channel_layer.group_add)(
58 self.group_name,
59 self.channel_name
60 )
61 else:
62 self.realtime = False
63 self.accept()
64
65 def disconnect(self, close_code):
66
67 if self.realtime:
68 async_to_sync(self.channel_layer.group_discard)(
69 self.group_name,
70 self.channel_name
71 )
72
73 def receive(self, text_data):
74 ret = {}
75 dt = datetime.strptime(text_data, '%d-%m-%Y')
76 code = self.scope['url_route']['kwargs']['code']
77 plot = self.scope['url_route']['kwargs']['plot']
78 # exp = DB.experiment.find_one({'code': int(code)})
79 # det0 = DB.exp_detail.find_one({'experiment': exp['_id'], 'date': dt-timedelta(days=1)})
80 # det1 = DB.exp_detail.find_one({'experiment': exp['_id'], 'date': dt})
81 exp = Experiment.objects.get(code=code)
82 det0 = ExpDetail.objects(experiment=exp, date=dt-timedelta(days=1))
83 det1 = ExpDetail.objects(experiment=exp, date=dt)
84
85 if det1:
86 meta1 = PlotMeta.objects(exp_detail=det1[0], plot=plot)
87 if meta1:
88 if plot == 'spc':
89 datas = PlotData.objects(plot=meta1[0]).order_by('-time').first()
90 ret['time'] = [datas['time']]
91 ret['spc'] = datas['data']
92 ret['meta'] = dict(meta1[0].metadata)
93 meta = PlotMeta.objects(exp_detail=det1[0], plot='rti')
94 if meta:
95 data = PlotData.objects(plot=meta[0], time=ret['time'][0])
96 if data:
97 ret['rti'] = data[0]['data']
98
99 meta = PlotMeta.objects(exp_detail=det1[0], plot='noise')
100 if meta:
101 data = PlotData.objects(plot=meta[0], time=ret['time'][0])
102 if data:
103 ret['meta']['titles'] = ['{} dB'.format(x) for x in data[0]['data']]
27 104 else:
28 print('New connection from: {}, history, id: {}'.format(message.content['client'][0], id))
29 message.reply_channel.send({
30 'accept': True
31 })
105 last = det1[0]['last_time']
106 metas = [meta1[0]]
107 if det0:
108 meta0 = PlotMeta.objects(exp_detail=det0[0], plot=plot)
109 if meta0:
110 metas.append(meta0[0])
111 datas = PlotData.objects(plot__in=metas, time__gt=last-12*60*60).limit(720)
112 dum = [(d['time'], d['data']) for d in datas]
113 ret['time'] = [d[0] for d in dum]
114 dum = [d[1] for d in dum]
115 ret[plot] = [t for t in map(list, list(zip(*dum)))]
116 ret['meta'] = metas[0].metadata
117
118 # exp.pop('date', None)
119 # exp.pop('_id', None)
120 self.send(json.dumps(ret))
121 else:
122 self.send(json.dumps({'interval': 0}))
123
124 def zmq_message(self, event):
125 # Send message to WebSocket
126 self.send(text_data=event['message'])
32 127
33 128 def ws_message(message, id, code, plot):
34 129 # Accept the incoming connection
35 130 dt = datetime.strptime(str(json.loads(message.content['text'])['date']), '%d/%m/%Y')
36 131 exp0 = DB.exp_meta.find_one({'code': int(code), 'date': dt-timedelta(days=1)})
37 132 exp = DB.exp_meta.find_one({'code': int(code), 'date': dt})
38 print('New request for id={}'.format(id))
133 print(('New request for id={}'.format(id)))
39 134 if exp and plot in exp['plots']:
40 135 if plot == 'spc':
41 136 datas = DB.exp_data.find({'expmeta': exp['_id']}, ['time', 'data']).sort('time', -1).limit(1)[0]
42 137 exp['time'] = [datas['time']]
43 138 exp['spc'] = datas['data']['spc']
44 139 exp['rti'] = datas['data']['rti']
45 140 exp['noise'] = datas['data']['noise']
46 141 else:
47 142 last = DB.exp_data.find_one({'expmeta': exp['_id']}, ['time'], sort=[('time', -1)])
48 143 if exp0:
49 144 datas = DB.exp_data.find(
50 145 {
51 146 'expmeta': {'$in': [exp0['_id'], exp['_id']]},
52 147 'time': {'$gt': last['time']-12*60*60}
53 148 },
54 149 ['time', 'data'],
55 150 sort=[('time', 1)],
56 151 limit=720
57 152 )
58 153 else:
59 154 datas = DB.exp_data.find(
60 155 {
61 156 'expmeta': exp['_id'],
62 157 'time': {'$gt': last['time']-12*60*60}
63 158 },
64 159 ['time', 'data'],
65 160 sort=[('time', 1)],
66 161 limit=720
67 162 )
68 163 dum = [(d['time'], d['data'][plot]) for d in datas]
69 164 exp['time'] = [d[0] for d in dum]
70 165 dum = [d[1] for d in dum]
71 exp[plot] = [t for t in map(list, zip(*dum))]
166 exp[plot] = [t for t in map(list, list(zip(*dum)))]
72 167
73 168 exp.pop('date', None)
74 169 exp.pop('_id', None)
75 170 message.reply_channel.send({'text': json.dumps(exp)})
76 171 else:
77 172 message.reply_channel.send({'text': json.dumps({'interval': 0})})
78
79 # Connected to websocket.disconnect
80 def ws_disconnect(message, id, code, plot):
81 Group('{}_{}'.format(code, plot)).discard(message.reply_channel)
@@ -1,33 +1,31
1 1 # -*- coding: utf-8 -*-
2 from __future__ import unicode_literals
3 2
4 3 from django.db import models
5 from mongoengine import *
4 from mongoengine import Document, IntField, FloatField, StringField, DictField, ListField, DateTimeField, ReferenceField
6 5
7 6 class Experiment(Document):
8 7 code = IntField(unique=True)
9 8 name = StringField(max_length=40)
10 9
11 class ExpMeta(Document):
12 code = IntField()
10 class ExpDetail(Document):
11 experiment = ReferenceField(Experiment)
13 12 date = DateTimeField()
14 pairs = ListField(default=list)
15 yrange = ListField(FloatField())
16 xrange = ListField(FloatField())
17 interval = FloatField()
18 plots = ListField(StringField())
19 localtime = BooleanField()
13 last_time = FloatField()
20 14
21 meta = {
22 'indexes': [[("code", 1), ("date", 1)]]
23 }
15 def plots(self):
16 return PlotMeta.objects(exp_detail=self)
17
18 class PlotMeta(Document):
19 exp_detail = ReferenceField(ExpDetail)
20 metadata = DictField()
21 plot = StringField()
24 22
25 class ExpData(Document):
26 expmeta = LazyReferenceField(ExpMeta)
23 class PlotData(Document):
24 plot = ReferenceField(PlotMeta)
27 25 time = FloatField()
28 data = DictField()
26 data = ListField()
29 27
30 28 meta = {
31 'indexes': ["expmeta", "+time"]
29 'indexes': ["plot", "+time"]
32 30 }
33 31
@@ -1,657 +1,673
1 1
2 2 var icon = {
3 3 'width': 20,
4 4 'path': 'M18.303,4.742l-1.454-1.455c-0.171-0.171-0.475-0.171-0.646,0l-3.061,3.064H2.019c-0.251,0-0.457,0.205-0.457,0.456v9.578c0,0.251,0.206,0.456,0.457,0.456h13.683c0.252,0,0.457-0.205,0.457-0.456V7.533l2.144-2.146C18.481,5.208,18.483,4.917,18.303,4.742 M15.258,15.929H2.476V7.263h9.754L9.695,9.792c-0.057,0.057-0.101,0.13-0.119,0.212L9.18,11.36h-3.98c-0.251,0-0.457,0.205-0.457,0.456c0,0.253,0.205,0.456,0.457,0.456h4.336c0.023,0,0.899,0.02,1.498-0.127c0.312-0.077,0.55-0.137,0.55-0.137c0.08-0.018,0.155-0.059,0.212-0.118l3.463-3.443V15.929z M11.241,11.156l-1.078,0.267l0.267-1.076l6.097-6.091l0.808,0.808L11.241,11.156z',
5 5 'ascent': 20,
6 6 'descent': 2,
7 7 };
8 8
9 9 function list2dict(values) {
10 10
11 11 var o = {};
12 12 $.each(values, function () {
13 13 o[this.name] = this.value;
14 14 });
15 15 return o;
16 16 };
17 17 /* In this class is defined all the function to RTI plot */
18 18 class PcolorBuffer {
19 19 constructor({ div, data, key, props }) {
20 20 this.div = document.getElementById(div);
21 21 this.n = 0;
22 22 this.divs = [];
23 23 this.wait = false;
24 24 this.key = key;
25 25 this.timer = (props.throttle || 30) * 1000;
26 26 this.lastRan = Date.now();
27 27 this.lastFunc = null;
28 28 this.zbuffer = [];
29 29 this.xbuffer = [];
30 this.empty = Array(data.yrange.length).fill(null);
30 this.empty = Array(data.meta.yrange.length).fill(null);
31 31 this.props = props;
32 32 this.setup(data);
33 33 }
34 34 /* This function is used to plot all the data that have the DB and just is used when is loaded or reloaded*/
35 35 setup(data) {
36 36 this.last = data.time.slice(-1);
37 37 if (data.time.length == 1) {
38 38 var values = { 'time': data.time, 'data': data[this.key].map(function (x) { return [x] }) };
39 39 } else {
40 var values = this.fill_gaps(data.time, data[this.key], data.interval, data[this.key].length);
40 var values = this.fill_gaps(data.time, data[this.key], data.meta.interval, data[this.key].length);
41 41 }
42 42 var t = values.time.map(function (x) {
43 43 var a = new Date(x * 1000);
44 44 // This condition is used to change from UTC to LT
45 45 if (data.localtime == true){
46 46 a.setTime( a.getTime() + a.getTimezoneOffset()*60*1000 );
47 47 }
48 48 return a;
49 49 });
50 50
51 51 var label;
52 52 if (data.localtime == true){
53 53 label = "[LT]";
54 54
55 55 }
56 56 else{
57 57 label = "[UTC]";
58 58 }
59 59
60 60 for (var i = 0; i < data[this.key].length; i++) {
61 61 var layout = {
62 62 height: 350,
63 63 xaxis: {
64 64 title: 'Time ' + label,
65 65 showgrid: false,
66 66 linewidth: 2,
67 67 size: 12,
68 68 mirror: true,
69 69 },
70 70 yaxis: {
71 71 title: 'km',
72 72 showgrid: false,
73 73 linewidth: 2,
74 74 size: 12,
75 75 mirror: true,
76 76 },
77 77 titlefont: {
78 78 size: 16,
79 79 },
80 80 margin: {
81 81 t: 30,
82 82 }
83 83 };
84 84 var iDiv = document.createElement('div');
85 85 iDiv.id = 'plot-' + i;
86 86 //iDiv.className += iDiv.className ? ' col-lg-6 col-md-6 col-sm-12' : 'col-lg-6 col-md-12 col-sm-12';
87 87 this.zbuffer.push([]);
88 88 this.n = this.n + 1;
89 89 this.div.appendChild(iDiv);
90 90 this.divs.push(iDiv.id);
91 91 var trace = {
92 92 z: values.data[i],
93 93 x: t,
94 94 y: data.yrange,
95 95 colorscale: this.props.colormap || 'Jet',
96 96 transpose: true,
97 97 type: 'heatmap'
98 98 };
99 99
100 100 if (this.props.zmin) { trace.zmin = this.props.zmin }
101 101 if (this.props.zmax) { trace.zmax = this.props.zmax }
102 102
103 103 layout.title = 'Ch ' + i + ' - ' + t.slice(-1).toLocaleString();
104 104 var conf = {
105 105 modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'],
106 106 modeBarButtonsToAdd: [{
107 107 name: 'Edit plot',
108 108 icon: icon,
109 109 click: function (gd) {
110 110 var div = gd.id;
111 111 $('input[id=id_plotdiv]').val(div);
112 112 $('#setup').modal('show');
113 113 }
114 114 }],
115 115 displaylogo: false,
116 116 showTips: true
117 117 };
118 118 Plotly.newPlot('plot-' + i, [trace], layout, conf);
119 119 }
120 120 }
121 121
122 122 getSize() {
123 123 var div = document.getElementById(this.divs[0]);
124 124 var t = this.xbuffer.slice(-1)[0];
125 125 var n = 0;
126 126 var timespan = (this.props.timespan || 12) * 1000 * 60 * 60; //If i dont put timespan in rti.html, by default is 8hs
127 127
128 128 while ((t - div.data[0].x[n]) > timespan) {
129 129 n += 1;
130 130 }
131 131 return n;
132 132 }
133 133
134 134 fill_gaps(xBuffer, zBuffer, interval, N) {
135 135
136 136 var x = [xBuffer[0]];
137 137 var z = [];
138 138 var last;
139 139
140 140 for (var j = 0; j < N; j++) {
141 141 z.push([zBuffer[j][0]]);
142 142 }
143 143
144 144 for (var i = 1; i < xBuffer.length; i++) {
145 145 var cnt = 0;
146 146 last = x.slice(-1)[0];
147 147 while (Math.abs(parseFloat(xBuffer[i]) - last ) > 1.5 * parseFloat(interval)) {
148 148 cnt += 1;
149 149 last = last + interval;
150 150 x.push(last);
151 151 for (var j = 0; j < N; j++) {
152 152 z[j].push(this.empty);
153 153 }
154 154 // Avoid infinite loop
155 155 if (cnt == 100) { break; }
156 156 }
157 157 x.push(xBuffer[i]);
158 158 for (var j = 0; j < N; j++) {
159 159 z[j].push(zBuffer[j][i]);
160 160 }
161 161 }
162 162 return { 'time': x, 'data': z };
163 163 }
164 164
165 165 plot() {
166 166 // add new data to plots and empty buffers
167 167 var N = this.getSize();
168 168 console.log('Plotting...');
169 169 for (var i = 0; i < this.n; i++) {
170 170 var div = document.getElementById(this.divs[i]);
171 171 if (N > 0) {
172 172 div.data[0].z = div.data[0].z.slice(N, )
173 173 div.data[0].x = div.data[0].x.slice(N, )
174 174 }
175 175 Plotly.extendTraces(div, {
176 176 z: [this.zbuffer[i]],
177 177 x: [this.xbuffer]
178 178 }, [0]);
179 179 this.zbuffer[i] = [];
180 180 }
181 181 this.xbuffer = [];
182 182 }
183 183 //This function just add the last data and is used if previously was used setup()
184 184 update(obj) {
185 185
186 186 // fill data gaps
187 187 var cnt = 0;
188 188
189 189 while (Math.abs(parseFloat(obj.time[0]) - this.last) > 1.5 * parseFloat(obj.interval)) {
190 190 cnt += 1;
191 191 this.last += obj.interval;
192 192 var newt = new Date((this.last) * 1000);
193 193 // This condition is used to change from UTC to LT
194 194 if (obj.localtime == true){
195 195 newt.setTime( newt.getTime() + newt.getTimezoneOffset()*60*1000 );
196 196 }
197 197 this.xbuffer.push(newt);
198 198 for (var i = 0; i < obj[this.key].length; i++) {
199 199 this.zbuffer[i].push(this.empty);
200 200 }
201 201 // Avoid infinite loop
202 202 if (cnt == 100) { break; }
203 203 }
204 204
205 205 // update buffers
206 206 this.last = parseFloat(obj.time[0]);
207 207 var t = new Date(obj.time[0] * 1000);
208 208 // This condition is used to change from UTC to LT
209 209 if (obj.localtime == true){
210 210 t.setTime( t.getTime() + t.getTimezoneOffset()*60*1000 );
211 211 }
212 212 this.xbuffer.push(t);
213 213 for (var i = 0; i < obj[this.key].length; i++) {
214 214 this.zbuffer[i].push(obj[this.key][i]);
215 215 var div = document.getElementById(this.divs[i]);
216 216 Plotly.relayout(div, {
217 217 title: 'Ch ' + i + ' - ' + t.toLocaleString(),
218 218 });
219 219 }
220 220
221 221 // plot when ready (every 10 secs)
222 222 if (!this.wait) {
223 223 this.plot();
224 224 this.wait = true;
225 225 } else {
226 226 clearTimeout(this.lastFunc)
227 227 this.lastFunc = setTimeout(function (scope) {
228 228 if ((Date.now() - scope.lastRan) >= scope.timer) {
229 229 scope.plot()
230 230 scope.lastRan = Date.now()
231 231 }
232 232 }, this.timer - (Date.now() - this.lastRan), this)
233 233 }
234 234 }
235 235 // With this function You can change parameters in your plot
236 236 restyle(values) {
237 237
238 238 var values = list2dict(values);
239 239 var div = document.getElementById(values.plotdiv);
240 240
241 241 Plotly.relayout(div, {
242 242 yaxis: {
243 243 range: [values.ymin, values.ymax],
244 244 title: 'km',
245 245 linewidth: 2,
246 246 size: 12,
247 247 mirror: true,
248 248 }
249 249
250 250 });
251 251
252 252 Plotly.restyle(div, {
253 253 zmin: values.zmin,
254 254 zmax: values.zmax,
255 255 colorscale: values.colormap
256 256 });
257 257 }
258 258 }
259 259 /* In this class is defined all the function to SPC plot */
260 260 class Pcolor {
261 261 constructor({ div, data, props }) {
262 262 this.div = document.getElementById(div);
263 263 this.n = 0;
264 264 this.divs = [];
265 265 this.props = props;
266 266 this.setup(data);
267 267 }
268 268 /* This function is used to plot all the data that have the DB and just is used when is loaded or reloaded*/
269 269 setup(data) {
270 270 for (var i = 0; i < data.spc.length; i++) {
271 271 var layout = {
272 272 margin: {
273 273 t:30,
274 274 },
275 275 height: 320,
276 276 xaxis: {
277 277 title: 'Velocity',
278 278 showgrid: false,
279 279 zeroline: false,
280 domain: [0, 0.7],
281 280 linewidth: 2,
282 281 mirror: true,
283 282 size: 12,
284 283 },
285 284 yaxis: {
286 285 title: 'km',
287 286 showgrid: false,
288 287 linewidth: 2,
289 288 mirror: 'all',
290 289 size: 12,
291 range: [data.yrange[0], data.yrange.slice(-1)[0]],
290 //range: [data.meta.yrange[0], data.meta.yrange.slice(-1)[0]],
292 291 },
293 xaxis2: {
294 title: 'dB',
295 domain: [0.75, 1],
296 ticks: 'outside',
297 linewidth: 2,
298 mirror: true,
299 size: 12,
300 },
301
302 292 titlefont: {
303 293 size: 14
304 294 },
305 295 };
306 296 var iDiv = document.createElement('div');
307 297 iDiv.id = 'plot-' + i;
308 298 iDiv.className += iDiv.className ? ' col-md-5' : 'col-md-5';
309 299 this.n = this.n + 1;
310 300 this.div.appendChild(iDiv);
311 301 this.divs.push(iDiv.id);
312 302 var iDiv = document.createElement('div');
313 303 iDiv.className = 'col-md-1';
314 304 this.div.appendChild(iDiv);
315 305 var trace1 = {
316 306 z: data.spc[i],
317 x: data.xrange,
307 y: data.meta.yrange,
308 x: data.meta.xrange,
318 309 colorscale: this.props.colormap || 'Jet',
319 310 transpose: true,
320 311 type: 'heatmap'
321 312 };
322 313
314 if ('rti' in data){
315 layout.xaxis.domain = [0, 0.7];
316 layout.xaxis2 = {
317 title: 'dB',
318 domain: [0.75, 1],
319 ticks: 'outside',
320 linewidth: 2,
321 mirror: true,
322 size: 12,
323 };
323 324 var trace2 = {
324 325 x: data.rti[i],
326 y: data.meta.yrange,
325 327 xaxis: 'x2',
326 328 type: 'scatter',
327 329 };
330 }
328 331
329 332 if (this.props.zmin) {
330 333 trace1.zmin = this.props.zmin
331 334 }
332 335 if (this.props.zmax) {
333 336 trace1.zmax = this.props.zmax;
337 if ('rti' in data){
334 338 layout.xaxis2.range = [this.props.zmin, this.props.zmax]
335 339 }
340 }
336 341
337 342 var t = new Date(data.time * 1000);
338 343 // This condition is used to change from UTC to LT
339 344 if (data.localtime == true){
340 345 t.setTime( t.getTime() + t.getTimezoneOffset()*60*1000 );
341 346 }
342 layout.title = 'Ch ' + i + ': ' + data.noise[i] + 'dB - ' + t.toLocaleString();
347 if ('titles' in data.meta){
348 layout.title = 'Ch ' + i + ': ' + data.meta.titles[i] + ' ' + t.toLocaleString();
349 }else{
350 layout.title = 'Ch ' + i + ': ' + t.toLocaleString();
351 }
343 352 var conf = {
344 353 modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'],
345 354 modeBarButtonsToAdd: [{
346 355 name: 'Edit plot',
347 356 icon: icon,
348 357 click: function (gd) {
349 358 var div = gd.id;
350 359 $('input[id=id_plotdiv]').val(div);
351 360 $('#setup').modal('show');
352 361 }
353 362 }],
354 363 displaylogo: false,
355 364 showTips: true
356 365 };
357 Plotly.newPlot('plot-' + i, [trace1, trace2], layout, conf);
366 if ('rti' in data){
367 var traces = [trace1, trace2]
368 }else{
369 var traces = [trace1]
370 }
371 Plotly.newPlot('plot-' + i, traces, layout, conf);
358 372 }
359 373 }
360 374
361 375 plot(obj) {
362 376 // this.data = obj;
363 377 // add new data to plots and empty buffers
364 378 console.log('Plotting...');
365 379 var t = new Date(obj.time[0] * 1000);
366 380 // This condition is used to change from UTC to LT
367 381 if (obj.localtime == true){
368 382 t.setTime( t.getTime() + t.getTimezoneOffset()*60*1000 );
369 383 }
370 384 for (var i = 0; i < this.n; i++) {
371 385 var div = document.getElementById(this.divs[i]);
372 386 Plotly.relayout(div, {
373 387 title: 'Ch ' + i + ': ' + obj.noise[i] + 'dB - ' + t.toLocaleString(),
374 388
375 389 });
376 390 Plotly.restyle(div, {
377 391 z: [obj.spc[i], null],
378 392 x: [obj.xrange, obj.rti[i]]
379 393 }, [0, 1]);
380 394 }
381 395 }
382 396
383 397 update(data) {
384 398 this.plot(data);
385 399 }
386 400
387 401 // With this function You can change parameters in your plot
388 402 restyle(values) {
389 403
390 404 var values = list2dict(values);
391 405 var div = document.getElementById(values.plotdiv);
392 406
393 407 Plotly.relayout(div, {
394 408 yaxis: {
395 409 title: 'km',
396 410 linewidth: 2,
397 411 mirror: 'all',
398 412 range: [values.ymin, values.ymax]
399 413 },
400 414 xaxis: {
401 415 title: 'Velocity',
402 416 linewidth: 2,
403 417 mirror: true,
404 418 domain: [0, .7],
405 419 range: [values.xmin, values.xmax]
406 420 },
407 421 xaxis2: {
408 422 title: 'dB',
409 423 linewidth: 2,
410 424 mirror: true,
411 425 domain: [0.75, 1],
412 426 range: [values.zmin, values.zmax]
413 427 }
414 428
415 429 });
416 430
417 431 Plotly.restyle(div, {
418 432 zmin: values.zmin,
419 433 zmax: values.zmax,
420 434 colorscale: values.colormap
421 435 });
422 436 }
423 437 }
424 438 /* In this class is defined all the function to Scatter(noise) plot */
425 439 class Scatter {
426 440 constructor({ div, data, key, props }) {
427 441 this.div = document.getElementById(div);
428 442 this.n = 0;
429 443 this.key = key;
430 444 this.wait = false;
431 445 this.timer = (props.throttle || 30) * 1000;
432 446 this.lastRan = Date.now();
433 447 this.lastFunc = null;
434 448 this.ybuffer = [];
435 449 this.xbuffer = [];
436 450 this.props = props;
437 451 this.setup(data);
438 452 }
439 453 /* This function is used to plot all the data that have the DB and just is used when is loaded or reloaded*/
440 454 setup(data) {
441 455
442 456 this.data = data; //le agrego juan carlos para ver la data en mi consola
443 457 var traces = [];
444 458 this.last = data.time.slice(-1);
445 459 if (data.time.length == 1) {
446 460 var values = { 'time': data.time, 'data': data[this.key] };
447 461 } else {
448 var values = this.fill_gaps(data.time, data[this.key], data.interval, data[this.key].length);
462 var values = this.fill_gaps(data.time, data[this.key], data.meta.interval, data[this.key].length);
449 463 }
450 464
451 465 var t = values.time.map(function (x) {
452 466 var a = new Date(x * 1000);
453 467 // This condition is used to change from UTC to LT
454 468 if (data.localtime == true){
455 469 a.setTime( a.getTime() + a.getTimezoneOffset()*60*1000 );
456 470 }
457 471 return a;
458 472 });
459 473
460 474 for (var i = 0; i < data[this.key].length; i++) {
461 475
462 476 this.n = this.n + 1;
463 477 this.ybuffer.push([]);
464 478 var trace = {
465 479 x: t,
466 480 y: values.data[i],
467 481 mode: 'lines',
468 482 type: 'scatter',
469 483 name: 'Channel ' + i,
470 484 connectgaps: false,
471 485 };
472 486
473 487 traces.push(trace);
474 488 }
475 489
476 490 var label;
477 491 if (data.localtime == true){
478 492 label = "[LT]";
479 493
480 494 }
481 495 else{
482 496 label = "[UTC]";
483 497 }
484 498
485 499 var layout = {
486 500 height: 300,
487 501 title: t.slice(-1).toLocaleString(),
488 502 font: {
489 503 size: 12,
490 504 },
491 505 xaxis: {
492 506 title: 'Time ' + label,
493 507 size: 12,
494 508 linewidth: 2,
495 509 mirror: true,
496 510 },
497 511 yaxis: {
498 512 title: this.props.ylabel || 'dB',
499 513 linewidth: 2,
500 514 mirror: true,
501 515 },
502 516 titlefont: {
503 517 size: 16,
504 518 },
505 519 margin: {
506 520 t: 30,
507 521 }
508 522 };
509 523
510 524 if (this.props.ymin) { layout.yaxis.range = [this.props.ymin, this.props.ymax] }
511 525
512 526 var conf = {
513 527 modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'],
514 528 modeBarButtonsToAdd: [{
515 529 name: 'Edit plot',
516 530 icon: icon,
517 531 click: function (gd) {
518 532 $('#setup').modal('show');
519 533 }
520 534 }],
521 535 displaylogo: false,
522 536 showTips: true
523 537 };
524 538 Plotly.newPlot('plot', traces, layout, conf);
525 539 }
526 540
527 541 getSize() {
528 542 var t = this.xbuffer.slice(-1)[0];
529 543 var n = 0;
530 544 var timespan = (this.props.timespan || 12) * 1000 * 60 * 60;
531 545
532 546 while ((t - this.div.data[0].x[n]) > timespan) {
533 547 n += 1;
534 548 }
535 549 return n;
536 550 }
537 551
538 552 fill_gaps(xBuffer, yBuffer, interval, N) {
539 553
540 554 var x = [xBuffer[0]];
541 555 var y = [];
542 556
543 557 for (var j = 0; j < N; j++) {
544 558 y.push([yBuffer[j][0]]);
545 559 }
546 560
547 561 var last;
548 562
549 563 for (var i = 1; i < xBuffer.length; i++) {
550 564 var cnt = 0;
551 565 last = x.slice(-1)[0];
566 console.log(Math.abs(parseFloat(xBuffer[i]) - last) + ' '+ parseFloat(interval));
552 567 while (Math.abs(parseFloat(xBuffer[i]) - last) > 1.5 * parseFloat(interval)) {
553 568 cnt += 1;
554 569 last = last + interval;
555 570 x.push(last);
571 console.log('missing ' + new Date(last*1000));
556 572 for (var j = 0; j < N; j++) {
557 573 y[j].push(null);
558 574 }
559 575 // Avoid infinite loop
560 576 if (cnt == 50) { break; }
561 577 }
562 578 x.push(xBuffer[i]);
563 579
564 580 for (var j = 0; j < N; j++) {
565 581 y[j].push(yBuffer[j][i]);
566 582 }
567 583 }
568 584 return { 'time': x, 'data': y };
569 585 }
570 586
571 587 plot() {
572 588 // add new data to plots and empty buffers
573 589 var xvalues = [];
574 590 var yvalues = [];
575 591 var traces = [];
576 592 var N = this.getSize();
577 593 console.log('Plotting...');
578 594 for (var i = 0; i < this.n; i++) {
579 595 if (N > 0) {
580 596 this.div.data[i].y = this.div.data[i].y.slice(N, )
581 597 this.div.data[i].x = this.div.data[i].x.slice(N, )
582 598 }
583 599 yvalues.push(this.ybuffer[i]);
584 600 xvalues.push(this.xbuffer);
585 601 traces.push(i);
586 602 this.ybuffer[i] = [];
587 603 }
588 604 Plotly.extendTraces(this.div, {
589 605 y: yvalues,
590 606 x: xvalues
591 607 }, traces);
592 608 this.xbuffer = [];
593 609 }
594 610 //This function just add the last data and is used if previously was used setup()
595 611 update(obj) {
596 612
597 613 // fill data gaps
598 614 var cnt = 0;
599 615 while (Math.abs(parseFloat(obj.time[0]) - this.last ) > 1.5 * parseFloat(obj.interval)) {
600 616 cnt += 1;
601 617 this.last += obj.interval;
602 618 var newt = new Date((this.last) * 1000);
603 619 // This condition is used to change from UTC to LT
604 620 if (obj.localtime == true){
605 621 newt.setTime( newt.getTime() + newt.getTimezoneOffset()*60*1000 );
606 622 }
607 623 this.xbuffer.push(newt);
608 624 for (var i = 0; i < this.n; i++) {
609 625 this.ybuffer[i].push(null);
610 626 }
611 627 // Avoid infinite loop
612 628 if (cnt == 100) { break; }
613 629 }
614 630
615 631 // update buffers
616 632 this.last = parseFloat(obj.time[0]);
617 633 var t = new Date(obj.time[0] * 1000);
618 634 // This condition is used to change from UTC to LT
619 635 if (obj.localtime == true){
620 636 t.setTime( t.getTime() + t.getTimezoneOffset()*60*1000 );
621 637 }
622 638 this.xbuffer.push(t);
623 639 for (var i = 0; i < this.n; i++) {
624 640 this.ybuffer[i].push(obj[this.key][i][0]);
625 641 }
626 642
627 643 Plotly.relayout(this.div, {
628 644 title: t.toLocaleString(),
629 645 });
630 646
631 647 // plot when ready (every 10 secs)
632 648 if (!this.wait) {
633 649 this.plot();
634 650 this.wait = true;
635 651 } else {
636 652 clearTimeout(this.lastFunc)
637 653 this.lastFunc = setTimeout(function (scope) {
638 654 if ((Date.now() - scope.lastRan) >= scope.timer) {
639 655 scope.plot()
640 656 scope.lastRan = Date.now()
641 657 }
642 658 }, this.timer - (Date.now() - this.lastRan), this)
643 659 }
644 660 }
645 661 // With this function You can change parameters in your plot
646 662 restyle(values) {
647 663
648 664 var values = list2dict(values);
649 665 Plotly.relayout(this.div, {
650 666 yaxis: {
651 667 range: [values.ymin, values.ymax],
652 668 title: this.props.ylabel || 'dB'
653 669 }
654 670
655 671 });
656 672 }
657 673 }
@@ -1,52 +1,53
1 1 {% extends 'base.html' %}
2 2 {% load static%}
3 3 {% block extra-header %}
4 4 {% endblock %}
5 5 {% block content %}
6 6 <div class="text-center p-2 text-igp">
7 7 <h1>Realtime Experiments at JRO</h1>
8 8 </div>
9 9 {% include 'cartas.html' %}
10 10 {% endblock content %}
11 11 {% block script %}
12 12 <script>
13 13 function pad(num) {
14 14 return ("0"+num).slice(-2);
15 15 }
16 16 function hhmmss(secs) {
17 17 var minutes = Math.floor(secs / 60);
18 18 secs = secs%60;
19 19 var hours = Math.floor(minutes/60)
20 20 minutes = minutes%60;
21 21 return pad(hours)+":"+pad(minutes)+":"+pad(secs);
22 22 }
23 23 $("#loader").css("display", "none");
24 24
25 25 /* This part create a new socket named "socket" to comunicate
26 26 if there is new data we could be able to change some attributes of a class*/
27 var socket = new WebSocket('ws://' + window.location.host +'/main/9999/none/');
27 var socket = new WebSocket('ws://' + window.location.host +'/ws/main/');
28 28 socket.onopen = function open() {
29 29 console.log('Main WebSockets connection created.');
30 30 };
31 31
32 socket.onmessage = function message(event) {
32 socket.onmessage = function(event) {
33 33 var data = JSON.parse(event.data);
34 34 console.log(data);
35 code = data.code;
36 value = data.value;
37 time = moment(new Date(data.time*1000)).format('hh:mm:ss a');
35 var code = data['code'];
36 console.log(code);
37 var value = data['value'];
38 var time = moment(new Date(data['time']*1000)).format('hh:mm:ss a');
38 39
39 40 /*This conditional ask if value(send by server) is online, and if it is then
40 41 change value to online in div with id="#alert_"+code*/
41 42
42 43 $("#date_"+code).text("Last data: "+time);
43 44 $("#card_"+code).removeClass().addClass("card mb-4 box-shadow text-"+value+" border-"+value);
44 45 $("#card_header_"+code).removeClass().addClass("card-header text-white bg-"+value);
45 46 $("#card_body_"+code).find("a").removeClass().addClass("btn btn-outline-"+value);
46 47 };
47 48
48 49 if (socket.readyState == WebSocket.OPEN) {
49 50 socket.onopen();
50 51 };
51 52 </script>
52 53 {% endblock script %}
@@ -1,92 +1,92
1 1 {% extends 'base.html' %} {% load static %} {% load bootstrap4 %} {% block content %}
2 2
3 3 <div class="p-4 text-igp">
4 4 <h3>{{title}} <small> {{subtitle}}</small></h3>
5 5 </div>
6 6 <div class="row justify-content-center">
7 7 <div id="loader" class="loader mt-5"></div>
8 8 </div>
9 9 <div id="plot" {%if plot == 'spc' %}class="row"{%endif%}></div>
10 10
11 11 {% endblock content %} {% block modal %}
12 12 <!-- Modal -->
13 13 <div class="modal fade" id="setup" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
14 14 <div class="modal-dialog modal-sm" role="document">
15 15 <div class="modal-content">
16 16 <div class="modal-header">
17 17 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
18 18 <span aria-hidden="true">&times;</span>
19 19 </button>
20 20 <h4 class="modal-title" id="myModalLabel">Setup plot</h4>
21 21 </div>
22 22 <div class="modal-body">
23 23 {% if code and plot %}
24 24 <form id="form_setup">
25 25 {% bootstrap_form setup_form layout='grid' size='small' %}
26 26 </form>
27 27 {% endif %}
28 28 </div>
29 29 <div class="modal-footer">
30 30 <button id="bt_update" type="button" class="btn btn-primary">Update</button>
31 31 </div>
32 32 </div>
33 33 </div>
34 34 </div>
35 35 {% endblock modal%} {% block script %}
36 36 <script src="{% static 'js/jroplots.js' %}"></script>
37 37 <script>
38 38 /* This conditional is used to know if we have to setup the data
39 39 or just update the last data*/
40 40 $("#loader").css("display", "block");
41 41 {% if realtime %}
42 var socket = new WebSocket('ws://' + window.location.host + '/realtime/{{code}}/{{plot}}/');
42 var socket = new WebSocket('ws://' + window.location.host + '/ws/realtime/{{code}}/{{plot}}/');
43 43 {% else %}
44 var socket = new WebSocket('ws://' + window.location.host + '/{{id}}/{{code}}/{{plot}}/');
44 var socket = new WebSocket('ws://' + window.location.host + '/ws/database/{{code}}/{{plot}}/');
45 45 {% endif %}
46 46 socket.onopen = function open() {
47 47 console.log('WebSockets connection created: ' + socket.url);
48 socket.send('{"date": "{{date}}"}')
48 socket.send('{{date}}')
49 49 };
50 50
51 51 socket.onmessage = function message(event) {
52 52 var data = JSON.parse(event.data);
53 53 console.log(data);
54 54 if (data.interval == 0) {
55 55 $("#loader").removeClass("loader").addClass("no-data");
56 56 $("#loader").html("No data found");
57 57 } else {
58 58 var first = plot(data);
59 59 if (first == true) {
60 60 $("#loader").css("display", "none");
61 61 }
62 62 }
63 63 }
64 64
65 65 if (socket.readyState == WebSocket.OPEN) {
66 66 socket.onopen();
67 67 }
68 68
69 69 let flag = true;
70 70 function plot(data) {
71 71 if (flag === true) {
72 72 flag = false;
73 73 plt = new {{ fn_plot }} ({
74 74 div: 'plot',
75 75 data: data,
76 76 key: '{{plot}}',
77 77 props: { throttle: 30, timespan: 12, colormap: 'Jet' },
78 78 });
79 79 return true;
80 80 } else {
81 81 plt.update(data);
82 82 return false;
83 83 }
84 84 }
85 85 /*It is the button to make changes in my plot parameters defined in block modal*/
86 86 $("#bt_update").click(function () {
87 87 $("#setup").modal('hide');
88 88 var values = $("#form_setup").serializeArray();
89 89 plt.restyle(values);
90 90 });
91 91
92 92 </script> {% endblock script %} No newline at end of file
@@ -1,156 +1,156
1 1 #!/usr/bin/python
2 2 # -*- coding: UTF-8 -*-
3 from __future__ import unicode_literals
3
4 4
5 5 import os
6 6 import time
7 7 from datetime import datetime
8 8
9 9 from django import forms
10 10 from django.contrib import messages
11 11 from django.utils.safestring import mark_safe
12 12 from django.shortcuts import render
13 13
14 14 import mongoengine
15 15
16 from plotter.models import Experiment, ExpMeta, ExpData
16 from plotter.models import Experiment, ExpDetail, PlotMeta, PlotData
17 17
18 18 host = os.environ.get('HOST_MONGO', 'localhost')
19 19 mongoengine.connect('dbplots', host=host, port=27017)
20 20
21 21
22 22 # Forms
23 23 class SearchForm(forms.Form):
24 24
25 25 experiment = forms.ChoiceField()
26 26 plot = forms.ChoiceField()
27 27
28 28 def __init__(self, *args, **kwargs):
29 29
30 30 exp_choices = kwargs.pop('exp_choices', [])
31 31 plt_choices = kwargs.pop('plt_choices', [])
32 32 super(SearchForm, self).__init__(*args, **kwargs)
33 33 self.fields['experiment'].choices = [(0, 'Select Experiment')] + exp_choices
34 34 self.fields['plot'].choices = [(0, 'Select Plot')] + plt_choices
35 35 # we use this class to change the parameter in Scatter plot using the function plotly.restyle in jroplot.js
36 36 class ScatterSetupForm(forms.Form):
37 37
38 38 plotdiv = forms.CharField(widget=forms.HiddenInput())
39 39 ymax = forms.CharField(initial=30)
40 40 ymin = forms.CharField(initial=10)
41 41
42 42 # we use this class to change the parameter in RTI plot using the function plotly.restyle in jroplot.js
43 43 class RTISetupForm(forms.Form):
44 44
45 45 plotdiv = forms.CharField(widget=forms.HiddenInput())
46 46 colormap = forms.ChoiceField(choices=[('Jet', 'Jet'), ('Viridis', 'Viridis'), ('RdBu', 'RdBu')])
47 47 zmax = forms.CharField(initial=30)
48 48 zmin = forms.CharField(initial=10)
49 49 ymax = forms.CharField(initial=180)
50 50 ymin = forms.CharField(initial=80)
51 51
52 52 # we use this class to change the parameter in SPC plot using the function plotly.restyle in jroplot.js
53 53 class SPCSetupForm(forms.Form):
54 54
55 55 plotdiv = forms.CharField(widget=forms.HiddenInput())
56 56 colormap = forms.ChoiceField(choices=[('Jet', 'Jet'), ('Viridis', 'Viridis'), ('RdBu', 'RdBu')])
57 57 #como es un perfil xmin y xmax deben ser iguales a zmin y zmax
58 58 xmax = forms.CharField(initial=30)
59 59 xmin = forms.CharField(initial=10)
60 60 #x2max = forms.CharField(initial=30)
61 61 #x2min = forms.CharField(initial=10)
62 62 ymax = forms.CharField(initial=180)
63 63 ymin = forms.CharField(initial=80)
64 64 zmax = forms.CharField(initial=30)
65 65 zmin = forms.CharField(initial=10)
66 66
67 67 # Create your views here.
68 68 def main(request):
69 69
70 70 kwargs = {}
71 date = request.GET.get('date', datetime.now().strftime('%d/%m/%Y'))
72 exps = ExpMeta.objects.filter(date=datetime.strptime(date, '%d/%m/%Y'))
71 date = request.GET.get('date', datetime.now().strftime('%d-%m-%Y'))
72 exps = ExpDetail.objects(date=datetime.strptime(date, '%d-%m-%Y'))
73 73 experiments = []
74 74
75 75 for exp in exps:
76 76 dum = {}
77 dum['code'] = exp.code
78 dum['plots'] = exp.plots
79 dum['name'] = Experiment.objects.get(code=exp.code).name
77 dum['code'] = exp.experiment.code
78 dum['plots'] = [plot.plot for plot in exp.plots()]
79 dum['name'] = exp.experiment.name
80 80 dt = datetime.now()
81 data = ExpData.objects(expmeta=exp).order_by('-time')[0] #Get the time from the last data
82 81
83 82 t = time.mktime(dt.timetuple())
84 83
85 if exp['localtime'] == True: #Ask which type of time is coming: LT o UTC
84 if exp.plots()[0]['metadata']['localtime'] == True: #Ask which type of time is coming: LT o UTC
86 85 t -= 5*60*60
87 86 # COnditionals to know which state are my clients
88 if (t-data['time']) > 6*exp['interval']:
87 if (t-exp['last_time']) > 10*60:
89 88 status = 'Offline'
90 89 clase = 'alertas-offline'
91 90 style = 'danger'
92 lastDataDate = data['time']
93 elif (t-data['time']) > 3*exp['interval']:
91 lastDataDate = exp['last_time']
92 elif (t-exp['last_time']) > 5*60:
94 93 status = 'Delayed'
95 94 clase = 'alertas-delayed'
96 95 style = 'warning'
97 lastDataDate = data['time']
96 lastDataDate = exp['last_time']
98 97 else:
99 98 status = 'Online'
100 99 clase = 'alertas-online'
101 100 style = 'success'
102 lastDataDate = data['time']
101 lastDataDate = exp['last_time']
103 102
104 103 dum['status'] = status
105 104 dum['class'] = clase
106 105 dum['style']= style
107 106 dum['date']= datetime.utcfromtimestamp(lastDataDate)
108 107
109 108 experiments.append(dum)
110 109
111 110 kwargs['date'] = date
112 111 kwargs['experiments'] = experiments
113 112
114 113 return render(request, 'home.html', kwargs)
115 114
116 115
117 116 def plot(request, code=None, plot=None):
118 117 '''
119 118 '''
120 119
121 120 realtime = False
122 121 date = request.GET.get('date', None)
123 122 if date is None:
124 date = datetime.now().strftime('%d/%m/%Y')
123 date = datetime.now().strftime('%d-%m-%Y')
125 124 realtime = True
126 125 exp = Experiment.objects.get(code=int(code))
127 expmeta = ExpMeta.objects.get(code=int(code), date=datetime.strptime(date, '%d/%m/%Y'))
126 detail = ExpDetail.objects.get(experiment=exp, date=datetime.strptime(date, '%d-%m-%Y'))
127 meta = PlotMeta.objects.get(exp_detail=detail, plot=plot)
128 128
129 129 kwargs = {
130 130 'code': code,
131 131 'plot': plot,
132 132 'date': date,
133 'id': expmeta.id,
133 'id': meta.pk,
134 134 'realtime': realtime,
135 135 'title': exp.name,
136 136 }
137 137 # Logic to show my views
138 138 if plot == 'rti':
139 139 kwargs['setup_form'] = RTISetupForm()
140 140 kwargs['fn_plot'] = 'PcolorBuffer'
141 141 kwargs['subtitle'] = 'RTI plot'
142 142 return render(request, 'plot.html', kwargs)
143 143 elif plot == 'spc':
144 144 kwargs['setup_form'] = SPCSetupForm()
145 145 kwargs['fn_plot'] = 'Pcolor'
146 146 kwargs['subtitle'] = 'Spectra plot'
147 147 return render(request, 'plot.html', kwargs)
148 148 elif plot == 'noise':
149 149 kwargs['setup_form'] = ScatterSetupForm()
150 150 kwargs['fn_plot'] = 'Scatter'
151 151 kwargs['subtitle'] = 'Noise plot'
152 152 return render(request, 'plot.html', kwargs)
153 153 else:
154 154 return render(request, 'home.html', {})
155 155
156 156 No newline at end of file
@@ -1,8 +1,11
1 from channels.routing import route
2 from plotter.consumers import ws_connect, ws_disconnect, ws_message
1 from channels.auth import AuthMiddlewareStack
2 from channels.routing import ProtocolTypeRouter, URLRouter
3 import plotter.routing
3 4
4 channel_routing = [
5 route("websocket.connect", ws_connect, path=r'^/(?P<id>[a-z]+)/(?P<code>[0-9]+)/(?P<plot>[a-z]+)/$'),
6 route("websocket.receive", ws_message, path=r'^/(?P<id>[a-z]+)/(?P<code>[0-9]+)/(?P<plot>[a-z]+)/$'),
7 route("websocket.disconnect", ws_disconnect, path=r'^/(?P<id>[a-z]+)/(?P<code>[0-9]+)/(?P<plot>[a-z]+)/$'),
8 ] No newline at end of file
5 application = ProtocolTypeRouter({
6 'websocket': AuthMiddlewareStack(
7 URLRouter(
8 plotter.routing.websocket_urlpatterns
9 )
10 ),
11 }) No newline at end of file
@@ -1,136 +1,137
1 1 """
2 2 Django settings for realtime project.
3 3
4 4 Generated by 'django-admin startproject' using Django 1.11.7.
5 5
6 6 For more information on this file, see
7 7 https://docs.djangoproject.com/en/1.11/topics/settings/
8 8
9 9 For the full list of settings and their values, see
10 10 https://docs.djangoproject.com/en/1.11/ref/settings/
11 11 """
12 12
13 13 import os
14 14
15 15 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 16 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 17
18 18
19 19 # Quick-start development settings - unsuitable for production
20 20 # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
21 21
22 22 # SECURITY WARNING: keep the secret key used in production secret!
23 23 SECRET_KEY = '-rgo(&lgs^!4jn6atk_^=!a)+jtt%%h48a_w5-csgn7jc@iao5'
24 24
25 25 # SECURITY WARNING: don't run with debug turned on in production!
26 26 DEBUG = True
27 27
28 28 ALLOWED_HOSTS = ['*'] #YONG
29 29
30 30
31 31 # Application definition
32 32
33 33 INSTALLED_APPS = [
34 34 'django.contrib.admin',
35 35 'django.contrib.auth',
36 36 'django.contrib.contenttypes',
37 37 'django.contrib.sessions',
38 38 'django.contrib.messages',
39 39 'django.contrib.staticfiles',
40 40 'bootstrap4',
41 41 'channels',
42 42 'plotter',
43 43 ]
44 44
45 45 MIDDLEWARE = [
46 46 'django.middleware.security.SecurityMiddleware',
47 47 'django.contrib.sessions.middleware.SessionMiddleware',
48 48 'django.middleware.common.CommonMiddleware',
49 49 'django.middleware.csrf.CsrfViewMiddleware',
50 50 'django.contrib.auth.middleware.AuthenticationMiddleware',
51 51 'django.contrib.messages.middleware.MessageMiddleware',
52 52 'django.middleware.clickjacking.XFrameOptionsMiddleware',
53 53 ]
54 54
55 55 ROOT_URLCONF = 'realtime.urls'
56 56
57 57 TEMPLATES = [
58 58 {
59 59 'BACKEND': 'django.template.backends.django.DjangoTemplates',
60 60 'DIRS': [],
61 61 'APP_DIRS': True,
62 62 'OPTIONS': {
63 63 'context_processors': [
64 64 'django.template.context_processors.debug',
65 65 'django.template.context_processors.request',
66 66 'django.contrib.auth.context_processors.auth',
67 67 'django.contrib.messages.context_processors.messages',
68 68 ],
69 69 },
70 70 },
71 71 ]
72 72
73 73 WSGI_APPLICATION = 'realtime.wsgi.application'
74 74
75 75
76 76 # Database
77 77 # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
78 78
79 79 DATABASES = {
80 80 'default': {
81 81 'ENGINE': 'django.db.backends.sqlite3',
82 82 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
83 83 }
84 84 }
85 85
86 86
87 87 # Password validation
88 88 # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
89 89
90 90 AUTH_PASSWORD_VALIDATORS = [
91 91 {
92 92 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
93 93 },
94 94 {
95 95 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
96 96 },
97 97 {
98 98 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
99 99 },
100 100 {
101 101 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
102 102 },
103 103 ]
104 104
105 105
106 106 # Internationalization
107 107 # https://docs.djangoproject.com/en/1.11/topics/i18n/
108 108
109 109 LANGUAGE_CODE = 'en-us'
110 110
111 111 TIME_ZONE = os.environ.get('TZ', 'UTC')
112 112
113 113 USE_I18N = True
114 114
115 115 USE_L10N = True
116 116
117 117 USE_TZ = True
118 118
119 119
120 120 # Static files (CSS, JavaScript, Images)
121 121 # https://docs.djangoproject.com/en/1.11/howto/static-files/
122 122
123 123 STATIC_URL = '/static/'
124 124
125 125 #======================== YONG ================================
126 126 host = os.environ.get('HOST_REDIS', '127.0.0.1')
127 127
128 128 CHANNEL_LAYERS = {
129 129 "default": {
130 "BACKEND": "asgi_redis.RedisChannelLayer",
130 'BACKEND': 'channels_redis.core.RedisChannelLayer',
131 131 "CONFIG": {
132 132 "hosts": [(host, 6379)],
133 133 },
134 "ROUTING": "realtime.routing.channel_routing",
135 134 },
136 135 }
136
137 ASGI_APPLICATION = "realtime.routing.application"
@@ -1,10 +1,10
1 asgi-redis==1.4.3
2 Django==1.11.7
1 Django
3 2 django-bootstrap4
4 channels==1.1.8
5 mongoengine==0.15.0
6 pymongo==3.5.1
7 pyzmq==16.0.3
8 redis==2.10.6
9 requests==2.18.4
10 simplejson==3.12.0
3 channels
4 channels_redis
5 mongoengine
6 pymongo
7 pyzmq
8 redis
9 requests
10 simplejson No newline at end of file
@@ -1,104 +1,110
1 1 #!/usr/bin/python
2 2 # -*- coding: UTF-8 -*-
3 3
4 4 import time
5 5 from datetime import datetime, timedelta
6 6 import zmq
7 7 import json
8 8 import numpy as np
9 9 from threading import Thread
10 10 import argparse
11 11
12 12 REQUEST_TIMEOUT = 5000
13 13 RETRIES = 5
14 14 SERVER_ENDPOINT = 'tcp://localhost:4444'
15 15
16 context = zmq.Context()
17 socket = context.socket(zmq.REQ)
18 socket.connect (SERVER_ENDPOINT)
19 poll = zmq.Poller()
20 poll.register(socket, zmq.POLLIN)
16
21 17
22 18 def send(dato):
23 19 '''
24 20 Function to send data to server
25 21 '''
26 global socket, poll
22 context = zmq.Context()
23 socket = context.socket(zmq.REQ)
24 socket.connect (SERVER_ENDPOINT)
25 poll = zmq.Poller()
26 poll.register(socket, zmq.POLLIN)
27 27 retries = RETRIES
28 28 while True:
29 29 socket.send_json(dato)
30 30 socks = dict(poll.poll(REQUEST_TIMEOUT))
31 31 if socks.get(socket) == zmq.POLLIN:
32 32 reply = socket.recv_string()
33 33 if reply == 'ok':
34 print('Server replied (%s)' % reply)
34 print(('Server replied (%s)' % reply))
35 35 break
36 36 else:
37 print('Malformed reply from server: %s' % reply)
37 print(('Malformed reply from server: %s' % reply))
38 38 else:
39 print('No response from server, retries left {}'.format(retries))
39 print(('No response from server, retries left {}'.format(retries)))
40 40 socket.setsockopt(zmq.LINGER, 0)
41 41 socket.close()
42 42 poll.unregister(socket)
43 43 retries -= 1
44 44 if retries == 0:
45 45 print('Server seems to be offline...')
46 46 socket = context.socket(zmq.REQ)
47 47 socket.connect(SERVER_ENDPOINT)
48 48 poll.register(socket, zmq.POLLIN)
49 49 break
50 50 # Create new connection
51 51 socket = context.socket(zmq.REQ)
52 52 socket.connect(SERVER_ENDPOINT)
53 53 poll.register(socket, zmq.POLLIN)
54 54
55 55 def main(realtime, code, date=None, interval=30):
56 56 '''
57 57 Simulate data to be sended to server
58 58 '''
59 59
60 60 if realtime:
61 61 dt = datetime.now()
62 62 else:
63 63 dt = date
64 64
65 data = {
66 'spc': np.round(np.random.rand(4, 60, 100)*5 + 10, 2).tolist(),
67 'rti': np.round(np.random.rand(4, 100)*5 + 10, 2).tolist(),
68 'noise': np.round(np.random.rand(4) + np.array([10,11,12,13]), 2).tolist()
69 }
70
65 71 while True:
66 72
67 print('Sending {} - {}'.format(code, dt))
73 print(('Sending {} - {}'.format(code, dt)))
68 74
69 75 dato = {
70 76 'time': time.mktime(dt.timetuple()),
71 'yrange': np.arange(100).tolist(),
77 'metadata':{
78 'yrange': np.arange(80, 120, 40/100.).tolist(),
72 79 'xrange': np.arange(-30, 30).tolist(),
73 80 'localtime': True,
74 'interval': interval,
81 'interval': interval
82 },
75 83 'exp_code': code,
76 'data': {
77 'noise': np.round(np.random.rand(4) + np.array([10,11,12,13]), 2).tolist(),
78 'rti': np.round(np.random.rand(4, 100)*5 + 10, 2).tolist(),
79 'spc': np.round(np.random.rand(4, 60, 100)*5 + 10, 2).tolist(),
80 }
81 84 }
82 85
83 86 dt = dt + timedelta(seconds=interval)
87 for plot, d in data.items():
88 dato['plot'] = plot
89 dato['data'] = d
84 90 t = Thread(target=send, args=(dato, ))
85 91 t.start()
86 92 if realtime:
87 93 time.sleep(interval)
88 94 else:
89 95 time.sleep(5)
90 96
91 97 if __name__ == '__main__':
92 98 parser = argparse.ArgumentParser(description='This is a Client for realtime app')
93 99 parser.add_argument('--date', action='store', default=None, help='format: 2018/02/13 12:23:00')
94 100 parser.add_argument('-r', action='store_true', dest='realtime', default=None)
95 parser.add_argument('-c', action='store', dest='code', default='170')
101 parser.add_argument('-c', action='store', dest='code', default='172')
96 102 parser.add_argument('-i', action='store', dest='interval', type=int, default=30)
97 103
98 104 results = parser.parse_args()
99 105 if not results.realtime:
100 106 try:
101 107 results.date = datetime.strptime(results.date, '%Y/%m/%d %H:%M:%S')
102 108 except:
103 raise(NameError('You must specify a date (--date) for non-realtime experiment'))
109 raise NameError('You must specify a date (--date) for non-realtime experiment')
104 110 main(results.realtime, results.code, results.date, results.interval)
@@ -1,153 +1,175
1 1 #!/usr/bin/python
2 2 # -*- coding: UTF-8 -*-
3 3
4 4 import os
5 5 import sys
6 6 import json
7 7 import simplejson
8 8 from datetime import datetime
9 9 import time
10 10 import zmq
11 import redis
12 import asgi_redis
13 11 import mongoengine
14 12 from threading import Thread
15 13
16 14 sys.path.append(os.environ.get('APP_DIR', '../'))
17 15 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "realtime.settings")
18 16
19 from plotter.models import Experiment, ExpMeta, ExpData
17 from plotter.models import Experiment, ExpDetail, PlotMeta, PlotData
20 18
21 19 host_mongo = os.environ.get('HOST_MONGO', 'localhost')
22 20 mongoengine.connect('dbplots', host=host_mongo, port=27017)
23 21
24 host_redis = os.environ.get('HOST_REDIS', 'localhost')
25 channel = asgi_redis.RedisChannelLayer(hosts=[(host_redis, 6379)])
22 import channels.layers
23 from asgiref.sync import async_to_sync
24
25 channel = channels.layers.get_channel_layer()
26 26
27 27 def loaddata():
28 28 print('Loading Experiments...')
29 29 if os.environ.get('APP_DIR', None) is not None:
30 30 file_exp = os.path.join(os.environ.get('APP_DIR'), 'scripts', 'experiments.json')
31 31 else:
32 32 file_exp = './experiments.json'
33 33 for tup in json.load(open(file_exp)):
34 print(tup['name'])
34 print(tup)
35 35 exp = Experiment.objects(code=tup['code']).modify(
36 36 upsert=True, # To add a new row
37 37 new=True,
38 38 set__code=tup['code'],
39 39 set__name=tup['name'],
40 40 )
41 41 exp.save()
42 42
43 43 #============== funcion para modificar datos en la tabla ==============
44 44 def update(buffer):
45 45 dt = datetime.utcfromtimestamp(buffer['time'])
46 print('Updating code={} date={} {}'.format(buffer['exp_code'], dt, datetime.now()))
47 exp = ExpMeta.objects(code=buffer['exp_code'], date=dt.date()).modify(
48 upsert=True, # To add a new row
46 exp = Experiment.objects.get(code=buffer['exp_code'])
47
48 detail = ExpDetail.objects(experiment=exp, date=dt.date()).modify(
49 upsert=True,
49 50 new=True,
50 set__code=buffer['exp_code'],
51 set__experiment=exp,
51 52 set__date=dt.date(),
52 set__yrange = buffer['yrange'],
53 set__xrange = buffer['xrange'],
54 set__interval = buffer['interval'],
55 set__localtime = buffer['localtime'],
56 set__plots = buffer['data'].keys()
53 set__last_time = buffer['time']
57 54 )
58 exp.save()
59 55
60 data = ExpData.objects(expmeta=exp, time=buffer['time']).modify(
56 plot = PlotMeta.objects(exp_detail=detail, plot=buffer['plot']).modify(
57 upsert=True,
58 new=True,
59 set__metadata = buffer['metadata']
60 )
61 #plot.save()
62
63 data = PlotData.objects(plot=plot, time=buffer['time']).modify(
61 64 upsert=True, # To add a new row
62 65 new=True,
63 set__expmeta = exp,
64 66 set__time = buffer['time'],
65 67 set__data = buffer['data']
66 68 )
67 69
68 data.save()
70 #data.save()
69 71
70 72 if datetime.now().date() == dt.date():
71 73 return True
72 74
73 75 return False
74 76
75 77 # Function that is checking the state of my clients every 30s
76 78 def check_times():
77 79
78 80 while True:
79 81 dt = datetime.now()
80 exps = ExpMeta.objects(date=dt.date())
81
82 for exp in exps:
83 data = ExpData.objects(expmeta=exp).order_by('-time')[0] #me quedo con el ultimo time del ultimo valor
82 exps = ExpDetail.objects(date=dt.date())
84 83
84 for detail in exps:
85 code = detail.experiment.code
86 plot = detail.plots()[0]
87 data_time = detail['last_time']
85 88 t = time.mktime(dt.timetuple())
86 89
87 if exp['localtime'] == True: #Consulto que tipode time me esta llegando: LT o UTC
90 if plot['metadata']['localtime'] == True:
88 91 t -= 5*60*60
89 92
90 if exp['localtime'] == True: #Consulto que tipode time me esta llegando: LT o UTC
91 data_time = data['time'] + 5*60*60
93 if plot['metadata']['localtime'] == True:
94 data_time = detail['last_time'] + 5*60*60
92 95
93 if (t-data['time']) > 6*exp['interval']:
94 channel.send_group(u'main', {'text': json.dumps({'code': exp['code'], 'value': 'danger', 'time': data_time})})
95 print ('{} {} {} {} {}'.format(exp.code, t, data['time'], (t-data['time']), 'offline'))
96 elif (t-data['time']) > 3*exp['interval']:
97 channel.send_group(u'main', {'text': json.dumps({'code': exp['code'], 'value': 'warning', 'time': data_time})})
98 print ('{} {} {} {} {}'.format(exp.code, t, data['time'], (t-data['time']), 'delayed'))
96 message = {
97 'code': code,
98 'time': data_time
99 }
100
101 if (t-detail['last_time']) > 10*60:
102 value = 'danger'
103 print(('{} {} {} {} {}'.format(code, t, detail['last_time'], (t-detail['last_time']), 'offline')))
104 elif (t-detail['last_time']) > 5*60:
105 value = 'warning'
106 print(('{} {} {} {} {}'.format(code, t, detail['last_time'], (t-detail['last_time']), 'delayed')))
99 107 else:
100 channel.send_group(u'main', {'text': json.dumps({'code': exp['code'], 'value': 'success', 'time': data_time})})
101 print ('{} {} {} {} {}'.format(exp.code, t, data['time'], (t-data['time']), 'online'))
108 value = 'success'
109
110 message['value'] = value
111
112 async_to_sync(channel.group_send)(
113 'main',
114 {
115 'type': 'zmq_message',
116 'message': json.dumps(message)
117 }
118 )
119
102 120 time.sleep(30)
103 121
104 122 def main():
105 123 print('Starting ZMQ server...')
106 124 context = zmq.Context()
107 125 receiver = context.socket(zmq.REP)
108 126 receiver.bind("tcp://0.0.0.0:4444")
109 127 t = Thread(target=check_times)
110 128 t.start()
111 129
112 130 while True:
113 131
114 132 buffer = receiver.recv_json()
115 133
116 if buffer['localtime'] == True: # Ask which type of time is coming: LT o UTC
134 if buffer['metadata']['localtime'] == True: # Ask which type of time is coming: LT o UTC
117 135 buffer['time'] -= 5*60*60
118 136
119 137 if not isinstance(buffer, dict):
120 138 print('*******************')
121 139 print(buffer)
122 140 continue
123 141 if not update(buffer):
124 142 receiver.send_string('ok')
125 143 continue
126 print("==================================")
127 for plot in buffer['data']:
128 dum = buffer.copy()
129 dum['time'] = [buffer['time']]
130 if plot=='noise':
131 dum[plot] = [[x] for x in buffer['data'][plot]]
132 elif plot=='spc':
133 dum['noise'] = [[x] for x in buffer['data']['noise']]
134 dum['spc'] = buffer['data']['spc']
135 dum['rti'] = buffer['data']['rti']
136 else:
137 dum[plot] = buffer['data'][plot]
138 dum.pop('data')
139 exp_code = dum.pop('exp_code')
140 channel.send_group(
141 u'{}_{}'.format(exp_code, plot),
142 {'text': simplejson.dumps(dum, ignore_nan=True)}
143 )
144 print('Sending to group {}_{} - {} bytes'.format(exp_code, plot, len(str(dum))))
144 # for plot in buffer['data']:
145 # dum = buffer.copy()
146 # dum['time'] = [buffer['time']]
147 # if plot=='noise':
148 # dum[plot] = [[x] for x in buffer['data'][plot]]
149 # elif plot=='spc':
150 # dum['noise'] = [[x] for x in buffer['data']['noise']]
151 # dum['spc'] = buffer['data']['spc']
152 # dum['rti'] = buffer['data']['rti']
153 # else:
154 # dum[plot] = buffer['data'][plot]
155 # dum.pop('data')
156 # exp_code = dum.pop('exp_code')
157 # group = '{}_{}'.format(exp_code, plot)
158 # async_to_sync(channel.group_send)(
159 # group,
160 # {
161 # 'type': 'zmq_message',
162 # 'message': simplejson.dumps(dum, ignore_nan=True)
163 # }
164 # )
165
166 # print('Sending to group {}_{} - {} bytes'.format(exp_code, plot, len(str(dum))))
145 167
146 168 receiver.send_string('ok')
147 169
148 170 receiver.close()
149 171 context.term()
150 172
151 173 if __name__=='__main__':
152 174 loaddata()
153 175 main()
General Comments 0
You need to be logged in to leave comments. Login now