The requested changes are too big and content was truncated. Show full diff
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 |
@@ -0,0 +1,6 | |||||
|
1 | from django.contrib import admin | |||
|
2 | from .models import PedestalDevConfiguration | |||
|
3 | ||||
|
4 | # Register your models here. | |||
|
5 | ||||
|
6 | admin.site.register(PedestalDevConfiguration) |
@@ -0,0 +1,80 | |||||
|
1 | import os | |||
|
2 | import json | |||
|
3 | ||||
|
4 | from django import forms | |||
|
5 | from django.utils.safestring import mark_safe | |||
|
6 | from apps.main.models import Device | |||
|
7 | from apps.main.forms import add_empty_choice | |||
|
8 | from .models import PedestalDevConfiguration | |||
|
9 | ||||
|
10 | def create_choices_from_model(model, conf_id, all_choice=False): | |||
|
11 | ||||
|
12 | instance = globals()[model] | |||
|
13 | choices = instance.objects.all().values_list('pk', 'name') | |||
|
14 | ||||
|
15 | return choices | |||
|
16 | ||||
|
17 | class PedestalDevConfigurationForm(forms.ModelForm): | |||
|
18 | ||||
|
19 | def __init__(self, *args, **kwargs): | |||
|
20 | super(PedestalDevConfigurationForm, self).__init__(*args, **kwargs) | |||
|
21 | ||||
|
22 | instance = getattr(self, 'instance', None) | |||
|
23 | ||||
|
24 | if instance and instance.pk: | |||
|
25 | ||||
|
26 | devices = Device.objects.filter(device_type__name='pedestal_dev') | |||
|
27 | if instance.experiment: | |||
|
28 | self.fields['experiment'].widget.attrs['read_only'] = True | |||
|
29 | #self.fields['experiment'].widget.choices = [(instance.experiment.id, instance.experiment)] | |||
|
30 | self.fields['device'].widget.choices = [(device.id, device) for device in devices] | |||
|
31 | ||||
|
32 | if 'initial' in kwargs and 'experiment' in kwargs['initial'] and kwargs['initial']['experiment'] not in (0, '0'): | |||
|
33 | self.fields['experiment'].widget.attrs['readonly'] = True | |||
|
34 | ||||
|
35 | class Meta: | |||
|
36 | model = PedestalDevConfiguration | |||
|
37 | exclude = ('type', 'parameters', 'status', 'total_units', 'author', 'hash') | |||
|
38 | ||||
|
39 | def clean(self): | |||
|
40 | form_data = super(PedestalDevConfigurationForm, self).clean() | |||
|
41 | return form_data | |||
|
42 | ||||
|
43 | def save(self, *args, **kwargs): | |||
|
44 | conf = super(PedestalDevConfigurationForm, self).save(*args, **kwargs) | |||
|
45 | conf.save() | |||
|
46 | return conf | |||
|
47 | ||||
|
48 | class ExtFileField(forms.FileField): | |||
|
49 | """ | |||
|
50 | Same as forms.FileField, but you can specify a file extension whitelist. | |||
|
51 | ||||
|
52 | >>> from django.core.files.uploadedfile import SimpleUploadedFile | |||
|
53 | >>> | |||
|
54 | >>> t = ExtFileField(ext_whitelist=(".pdf", ".txt")) | |||
|
55 | >>> | |||
|
56 | >>> t.clean(SimpleUploadedFile('filename.pdf', 'Some File Content')) | |||
|
57 | >>> t.clean(SimpleUploadedFile('filename.txt', 'Some File Content')) | |||
|
58 | >>> | |||
|
59 | >>> t.clean(SimpleUploadedFile('filename.exe', 'Some File Content')) | |||
|
60 | Traceback (most recent call last): | |||
|
61 | ... | |||
|
62 | ValidationError: [u'Not allowed filetype!'] | |||
|
63 | """ | |||
|
64 | def __init__(self, *args, **kwargs): | |||
|
65 | extensions = kwargs.pop("extensions") | |||
|
66 | self.extensions = [i.lower() for i in extensions] | |||
|
67 | ||||
|
68 | super(ExtFileField, self).__init__(*args, **kwargs) | |||
|
69 | ||||
|
70 | def clean(self, *args, **kwargs): | |||
|
71 | data = super(ExtFileField, self).clean(*args, **kwargs) | |||
|
72 | filename = data.name | |||
|
73 | ext = os.path.splitext(filename)[1] | |||
|
74 | ext = ext.lower() | |||
|
75 | if ext not in self.extensions: | |||
|
76 | raise forms.ValidationError('Not allowed file type: %s' % ext) | |||
|
77 | ||||
|
78 | class PedestalDevImportForm(forms.Form): | |||
|
79 | ||||
|
80 | file_name = ExtFileField(extensions=['.racp', '.json', '.dat']) No newline at end of file |
@@ -0,0 +1,273 | |||||
|
1 | import ast | |||
|
2 | import json | |||
|
3 | import requests | |||
|
4 | import base64 | |||
|
5 | import struct | |||
|
6 | from struct import pack | |||
|
7 | import time | |||
|
8 | from django.contrib import messages | |||
|
9 | from django.db import models | |||
|
10 | from django.urls import reverse | |||
|
11 | from django.core.validators import MinValueValidator, MaxValueValidator | |||
|
12 | ||||
|
13 | from apps.main.models import Configuration | |||
|
14 | ||||
|
15 | MODE_VALUE = ( | |||
|
16 | ('SPD', 'speed'), | |||
|
17 | ('POS', 'position') | |||
|
18 | ) | |||
|
19 | ||||
|
20 | AXIS_VALUE = ( | |||
|
21 | ('AZI', 'azimuth'), | |||
|
22 | ('ELE', 'elevation') | |||
|
23 | ) | |||
|
24 | ||||
|
25 | class PedestalDevConfiguration(Configuration): | |||
|
26 | ||||
|
27 | mode = models.CharField( | |||
|
28 | verbose_name='Mode', | |||
|
29 | max_length=3, | |||
|
30 | choices=MODE_VALUE, | |||
|
31 | null=False, | |||
|
32 | blank=False | |||
|
33 | ) | |||
|
34 | ||||
|
35 | axis = models.CharField( | |||
|
36 | verbose_name='Axis', | |||
|
37 | max_length=3, | |||
|
38 | choices=AXIS_VALUE, | |||
|
39 | null=False, | |||
|
40 | blank=False | |||
|
41 | ) | |||
|
42 | ||||
|
43 | speed = models.FloatField( | |||
|
44 | verbose_name='Speed', | |||
|
45 | validators=[MinValueValidator(-20), MaxValueValidator(20)], | |||
|
46 | blank=True, | |||
|
47 | null=True | |||
|
48 | ) | |||
|
49 | ||||
|
50 | position = models.FloatField( | |||
|
51 | verbose_name='Position', | |||
|
52 | validators=[MinValueValidator(0), MaxValueValidator(360)], | |||
|
53 | blank=True, | |||
|
54 | null =True | |||
|
55 | ) | |||
|
56 | ||||
|
57 | class Meta: | |||
|
58 | db_table = 'pedestal_dev_configurations' | |||
|
59 | ||||
|
60 | def __str__(self): | |||
|
61 | return str(self.label) | |||
|
62 | ||||
|
63 | def get_absolute_url_plot(self): | |||
|
64 | return reverse('url_plot_pedestal_dev_pulses', args=[str(self.id)]) | |||
|
65 | ||||
|
66 | def request(self, cmd, method='get', **kwargs): | |||
|
67 | ||||
|
68 | req = getattr(requests, method)(self.device.url(cmd), **kwargs) | |||
|
69 | payload = req.json() | |||
|
70 | ||||
|
71 | return payload | |||
|
72 | ||||
|
73 | def status_device(self): | |||
|
74 | ||||
|
75 | try: | |||
|
76 | #self.device.status = 0 | |||
|
77 | #payload = self.request('status') | |||
|
78 | payload = requests.get(self.device.url()) | |||
|
79 | print(payload) | |||
|
80 | if payload: | |||
|
81 | self.device.status = 1 | |||
|
82 | elif payload['status']=='disable': | |||
|
83 | self.device.status = 2 | |||
|
84 | else: | |||
|
85 | self.device.status = 1 | |||
|
86 | self.device.save() | |||
|
87 | self.message = 'Pedestal Dev status: {}'.format(payload['status']) | |||
|
88 | return False | |||
|
89 | except Exception as e: | |||
|
90 | if 'No route to host' not in str(e): | |||
|
91 | self.device.status = 4 | |||
|
92 | self.device.save() | |||
|
93 | self.message = 'Pedestal Dev status: {}'.format(str(e)) | |||
|
94 | return False | |||
|
95 | ||||
|
96 | self.device.save() | |||
|
97 | return True | |||
|
98 | ||||
|
99 | def reset_device(self): | |||
|
100 | ||||
|
101 | try: | |||
|
102 | payload = self.request('reset', 'post') | |||
|
103 | if payload['reset']=='ok': | |||
|
104 | self.message = 'Pedestal Dev restarted OK' | |||
|
105 | self.device.status = 2 | |||
|
106 | self.device.save() | |||
|
107 | else: | |||
|
108 | self.message = 'Pedestal Dev restart fail' | |||
|
109 | self.device.status = 4 | |||
|
110 | self.device.save() | |||
|
111 | except Exception as e: | |||
|
112 | self.message = 'Pedestal Dev reset: {}'.format(str(e)) | |||
|
113 | return False | |||
|
114 | ||||
|
115 | return True | |||
|
116 | ||||
|
117 | def stop_device(self): | |||
|
118 | ||||
|
119 | try: | |||
|
120 | command = self.device.url() + "stop" | |||
|
121 | r = requests.get(command) | |||
|
122 | if r: | |||
|
123 | self.device.status = 4 | |||
|
124 | self.device.save() | |||
|
125 | self.message = 'Pedestal Dev stopped' | |||
|
126 | else: | |||
|
127 | self.device.status = 4 | |||
|
128 | self.device.save() | |||
|
129 | return False | |||
|
130 | except Exception as e: | |||
|
131 | if 'No route to host' not in str(e): | |||
|
132 | self.device.status = 4 | |||
|
133 | else: | |||
|
134 | self.device.status = 0 | |||
|
135 | #self.message = 'Pedestal Dev stop: {}'.format(str(e)) | |||
|
136 | self.message = "Pedestal Dev can't start, please check network/device connection or IP address/port configuration" | |||
|
137 | self.device.save() | |||
|
138 | return False | |||
|
139 | ||||
|
140 | return True | |||
|
141 | ||||
|
142 | def start_device(self): | |||
|
143 | ||||
|
144 | try: | |||
|
145 | pedestal = PedestalDevConfiguration.objects.get(pk=self) | |||
|
146 | print(pedestal) | |||
|
147 | pedestal_axis = pedestal.get_axis_display() | |||
|
148 | print(pedestal_axis) | |||
|
149 | ||||
|
150 | if pedestal.mode=='SPD': | |||
|
151 | data = {'axis': pedestal_axis, 'speed': pedestal.speed} | |||
|
152 | json_data = json.dumps(data) | |||
|
153 | ||||
|
154 | elif pedestal.mode=='POS': | |||
|
155 | data = {'axis': pedestal_axis, 'speed': pedestal.position} | |||
|
156 | json_data = json.dumps(data) | |||
|
157 | ||||
|
158 | print(json_data) | |||
|
159 | base64_mode = base64.urlsafe_b64encode(json_data.encode('ascii')) | |||
|
160 | mode_url = self.device.url() + "aspeed?params=" | |||
|
161 | ||||
|
162 | complete_url = mode_url + base64_mode.decode('ascii') | |||
|
163 | print(complete_url) | |||
|
164 | ||||
|
165 | #time.sleep(10) | |||
|
166 | r = requests.get(complete_url) | |||
|
167 | ||||
|
168 | if r: | |||
|
169 | self.device.status = 3 | |||
|
170 | self.device.save() | |||
|
171 | self.message = 'Pedestal Dev configured and started' | |||
|
172 | else: | |||
|
173 | return False | |||
|
174 | except Exception as e: | |||
|
175 | if 'No route to host' not in str(e): | |||
|
176 | self.device.status = 4 | |||
|
177 | else: | |||
|
178 | self.device.status = 0 | |||
|
179 | #self.message = 'Pedestal Dev start: {}'.format(str(e)) | |||
|
180 | self.message = "Pedestal Dev can't start, please check network/device connection or IP address/port configuration" | |||
|
181 | self.device.save() | |||
|
182 | return False | |||
|
183 | ||||
|
184 | return True | |||
|
185 | ||||
|
186 | #def write_device(self, raw=False): | |||
|
187 | ||||
|
188 | if not raw: | |||
|
189 | clock = RCClock.objects.get(rc_configuration=self) | |||
|
190 | print(clock) | |||
|
191 | if clock.mode: | |||
|
192 | data = {'default': clock.frequency} | |||
|
193 | else: | |||
|
194 | data = {'manual': [clock.multiplier, clock.divisor, clock.reference]} | |||
|
195 | payload = self.request('setfreq', 'post', data=json.dumps(data)) | |||
|
196 | print(payload) | |||
|
197 | if payload['command'] != 'ok': | |||
|
198 | self.message = 'Pedestal Dev write: {}'.format(payload['command']) | |||
|
199 | else: | |||
|
200 | self.message = payload['programming'] | |||
|
201 | if payload['programming'] == 'fail': | |||
|
202 | self.message = 'Pedestal Dev write: error programming CGS chip' | |||
|
203 | ||||
|
204 | values = [] | |||
|
205 | for pulse, delay in zip(self.get_pulses(), self.get_delays()): | |||
|
206 | while delay>65536: | |||
|
207 | values.append((pulse, 65535)) | |||
|
208 | delay -= 65536 | |||
|
209 | values.append((pulse, delay-1)) | |||
|
210 | data = bytearray() | |||
|
211 | #reset | |||
|
212 | data.extend((128, 0)) | |||
|
213 | #disable | |||
|
214 | data.extend((129, 0)) | |||
|
215 | #SW switch | |||
|
216 | if self.control_sw: | |||
|
217 | data.extend((130, 2)) | |||
|
218 | else: | |||
|
219 | data.extend((130, 0)) | |||
|
220 | #divider | |||
|
221 | data.extend((131, self.clock_divider-1)) | |||
|
222 | #enable writing | |||
|
223 | data.extend((139, 62)) | |||
|
224 | ||||
|
225 | last = 0 | |||
|
226 | for tup in values: | |||
|
227 | vals = pack('<HH', last^tup[0], tup[1]) | |||
|
228 | last = tup[0] | |||
|
229 | data.extend((133, vals[1], 132, vals[0], 133, vals[3], 132, vals[2])) | |||
|
230 | ||||
|
231 | #enable | |||
|
232 | data.extend((129, 1)) | |||
|
233 | ||||
|
234 | if raw: | |||
|
235 | return b64encode(data) | |||
|
236 | ||||
|
237 | try: | |||
|
238 | payload = self.request('stop', 'post') | |||
|
239 | payload = self.request('reset', 'post') | |||
|
240 | #payload = self.request('divider', 'post', data={'divider': self.clock_divider-1}) | |||
|
241 | #payload = self.request('write', 'post', data=b64encode(bytearray((139, 62))), timeout=20) | |||
|
242 | n = len(data) | |||
|
243 | x = 0 | |||
|
244 | #while x < n: | |||
|
245 | payload = self.request('write', 'post', data=b64encode(data)) | |||
|
246 | # x += 1024 | |||
|
247 | ||||
|
248 | if payload['write']=='ok': | |||
|
249 | self.device.status = 3 | |||
|
250 | self.device.save() | |||
|
251 | self.message = 'Pedestal Dev configured and started' | |||
|
252 | else: | |||
|
253 | self.device.status = 1 | |||
|
254 | self.device.save() | |||
|
255 | self.message = 'Pedestal Dev write: {}'.format(payload['write']) | |||
|
256 | return False | |||
|
257 | ||||
|
258 | #payload = self.request('start', 'post') | |||
|
259 | ||||
|
260 | except Exception as e: | |||
|
261 | if 'No route to host' not in str(e): | |||
|
262 | self.device.status = 4 | |||
|
263 | else: | |||
|
264 | self.device.status = 0 | |||
|
265 | self.message = 'Pedestal Dev write: {}'.format(str(e)) | |||
|
266 | self.device.save() | |||
|
267 | return False | |||
|
268 | ||||
|
269 | return True | |||
|
270 | ||||
|
271 | ||||
|
272 | def get_absolute_url_import(self): | |||
|
273 | return reverse('url_import_pedestal_dev_conf', args=[str(self.id)]) |
@@ -0,0 +1,2 | |||||
|
1 | .bk-bs-container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media(min-width:768px){.bk-bs-container{width:750px}}@media(min-width:992px){.bk-bs-container{width:970px}}@media(min-width:1200px){.bk-bs-container{width:1170px}}.bk-bs-container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.bk-bs-row{margin-left:-15px;margin-right:-15px}.bk-bs-col-xs-1,.bk-bs-col-sm-1,.bk-bs-col-md-1,.bk-bs-col-lg-1,.bk-bs-col-xs-2,.bk-bs-col-sm-2,.bk-bs-col-md-2,.bk-bs-col-lg-2,.bk-bs-col-xs-3,.bk-bs-col-sm-3,.bk-bs-col-md-3,.bk-bs-col-lg-3,.bk-bs-col-xs-4,.bk-bs-col-sm-4,.bk-bs-col-md-4,.bk-bs-col-lg-4,.bk-bs-col-xs-5,.bk-bs-col-sm-5,.bk-bs-col-md-5,.bk-bs-col-lg-5,.bk-bs-col-xs-6,.bk-bs-col-sm-6,.bk-bs-col-md-6,.bk-bs-col-lg-6,.bk-bs-col-xs-7,.bk-bs-col-sm-7,.bk-bs-col-md-7,.bk-bs-col-lg-7,.bk-bs-col-xs-8,.bk-bs-col-sm-8,.bk-bs-col-md-8,.bk-bs-col-lg-8,.bk-bs-col-xs-9,.bk-bs-col-sm-9,.bk-bs-col-md-9,.bk-bs-col-lg-9,.bk-bs-col-xs-10,.bk-bs-col-sm-10,.bk-bs-col-md-10,.bk-bs-col-lg-10,.bk-bs-col-xs-11,.bk-bs-col-sm-11,.bk-bs-col-md-11,.bk-bs-col-lg-11,.bk-bs-col-xs-12,.bk-bs-col-sm-12,.bk-bs-col-md-12,.bk-bs-col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.bk-bs-col-xs-1,.bk-bs-col-xs-2,.bk-bs-col-xs-3,.bk-bs-col-xs-4,.bk-bs-col-xs-5,.bk-bs-col-xs-6,.bk-bs-col-xs-7,.bk-bs-col-xs-8,.bk-bs-col-xs-9,.bk-bs-col-xs-10,.bk-bs-col-xs-11,.bk-bs-col-xs-12{float:left}.bk-bs-col-xs-12{width:100%}.bk-bs-col-xs-11{width:91.66666667%}.bk-bs-col-xs-10{width:83.33333333%}.bk-bs-col-xs-9{width:75%}.bk-bs-col-xs-8{width:66.66666667%}.bk-bs-col-xs-7{width:58.33333333%}.bk-bs-col-xs-6{width:50%}.bk-bs-col-xs-5{width:41.66666667%}.bk-bs-col-xs-4{width:33.33333333%}.bk-bs-col-xs-3{width:25%}.bk-bs-col-xs-2{width:16.66666667%}.bk-bs-col-xs-1{width:8.33333333%}.bk-bs-col-xs-pull-12{right:100%}.bk-bs-col-xs-pull-11{right:91.66666667%}.bk-bs-col-xs-pull-10{right:83.33333333%}.bk-bs-col-xs-pull-9{right:75%}.bk-bs-col-xs-pull-8{right:66.66666667%}.bk-bs-col-xs-pull-7{right:58.33333333%}.bk-bs-col-xs-pull-6{right:50%}.bk-bs-col-xs-pull-5{right:41.66666667%}.bk-bs-col-xs-pull-4{right:33.33333333%}.bk-bs-col-xs-pull-3{right:25%}.bk-bs-col-xs-pull-2{right:16.66666667%}.bk-bs-col-xs-pull-1{right:8.33333333%}.bk-bs-col-xs-pull-0{right:0}.bk-bs-col-xs-push-12{left:100%}.bk-bs-col-xs-push-11{left:91.66666667%}.bk-bs-col-xs-push-10{left:83.33333333%}.bk-bs-col-xs-push-9{left:75%}.bk-bs-col-xs-push-8{left:66.66666667%}.bk-bs-col-xs-push-7{left:58.33333333%}.bk-bs-col-xs-push-6{left:50%}.bk-bs-col-xs-push-5{left:41.66666667%}.bk-bs-col-xs-push-4{left:33.33333333%}.bk-bs-col-xs-push-3{left:25%}.bk-bs-col-xs-push-2{left:16.66666667%}.bk-bs-col-xs-push-1{left:8.33333333%}.bk-bs-col-xs-push-0{left:0}.bk-bs-col-xs-offset-12{margin-left:100%}.bk-bs-col-xs-offset-11{margin-left:91.66666667%}.bk-bs-col-xs-offset-10{margin-left:83.33333333%}.bk-bs-col-xs-offset-9{margin-left:75%}.bk-bs-col-xs-offset-8{margin-left:66.66666667%}.bk-bs-col-xs-offset-7{margin-left:58.33333333%}.bk-bs-col-xs-offset-6{margin-left:50%}.bk-bs-col-xs-offset-5{margin-left:41.66666667%}.bk-bs-col-xs-offset-4{margin-left:33.33333333%}.bk-bs-col-xs-offset-3{margin-left:25%}.bk-bs-col-xs-offset-2{margin-left:16.66666667%}.bk-bs-col-xs-offset-1{margin-left:8.33333333%}.bk-bs-col-xs-offset-0{margin-left:0}@media(min-width:768px){.bk-bs-col-sm-1,.bk-bs-col-sm-2,.bk-bs-col-sm-3,.bk-bs-col-sm-4,.bk-bs-col-sm-5,.bk-bs-col-sm-6,.bk-bs-col-sm-7,.bk-bs-col-sm-8,.bk-bs-col-sm-9,.bk-bs-col-sm-10,.bk-bs-col-sm-11,.bk-bs-col-sm-12{float:left}.bk-bs-col-sm-12{width:100%}.bk-bs-col-sm-11{width:91.66666667%}.bk-bs-col-sm-10{width:83.33333333%}.bk-bs-col-sm-9{width:75%}.bk-bs-col-sm-8{width:66.66666667%}.bk-bs-col-sm-7{width:58.33333333%}.bk-bs-col-sm-6{width:50%}.bk-bs-col-sm-5{width:41.66666667%}.bk-bs-col-sm-4{width:33.33333333%}.bk-bs-col-sm-3{width:25%}.bk-bs-col-sm-2{width:16.66666667%}.bk-bs-col-sm-1{width:8.33333333%}.bk-bs-col-sm-pull-12{right:100%}.bk-bs-col-sm-pull-11{right:91.66666667%}.bk-bs-col-sm-pull-10{right:83.33333333%}.bk-bs-col-sm-pull-9{right:75%}.bk-bs-col-sm-pull-8{right:66.66666667%}.bk-bs-col-sm-pull-7{right:58.33333333%}.bk-bs-col-sm-pull-6{right:50%}.bk-bs-col-sm-pull-5{right:41.66666667%}.bk-bs-col-sm-pull-4{right:33.33333333%}.bk-bs-col-sm-pull-3{right:25%}.bk-bs-col-sm-pull-2{right:16.66666667%}.bk-bs-col-sm-pull-1{right:8.33333333%}.bk-bs-col-sm-pull-0{right:0}.bk-bs-col-sm-push-12{left:100%}.bk-bs-col-sm-push-11{left:91.66666667%}.bk-bs-col-sm-push-10{left:83.33333333%}.bk-bs-col-sm-push-9{left:75%}.bk-bs-col-sm-push-8{left:66.66666667%}.bk-bs-col-sm-push-7{left:58.33333333%}.bk-bs-col-sm-push-6{left:50%}.bk-bs-col-sm-push-5{left:41.66666667%}.bk-bs-col-sm-push-4{left:33.33333333%}.bk-bs-col-sm-push-3{left:25%}.bk-bs-col-sm-push-2{left:16.66666667%}.bk-bs-col-sm-push-1{left:8.33333333%}.bk-bs-col-sm-push-0{left:0}.bk-bs-col-sm-offset-12{margin-left:100%}.bk-bs-col-sm-offset-11{margin-left:91.66666667%}.bk-bs-col-sm-offset-10{margin-left:83.33333333%}.bk-bs-col-sm-offset-9{margin-left:75%}.bk-bs-col-sm-offset-8{margin-left:66.66666667%}.bk-bs-col-sm-offset-7{margin-left:58.33333333%}.bk-bs-col-sm-offset-6{margin-left:50%}.bk-bs-col-sm-offset-5{margin-left:41.66666667%}.bk-bs-col-sm-offset-4{margin-left:33.33333333%}.bk-bs-col-sm-offset-3{margin-left:25%}.bk-bs-col-sm-offset-2{margin-left:16.66666667%}.bk-bs-col-sm-offset-1{margin-left:8.33333333%}.bk-bs-col-sm-offset-0{margin-left:0}}@media(min-width:992px){.bk-bs-col-md-1,.bk-bs-col-md-2,.bk-bs-col-md-3,.bk-bs-col-md-4,.bk-bs-col-md-5,.bk-bs-col-md-6,.bk-bs-col-md-7,.bk-bs-col-md-8,.bk-bs-col-md-9,.bk-bs-col-md-10,.bk-bs-col-md-11,.bk-bs-col-md-12{float:left}.bk-bs-col-md-12{width:100%}.bk-bs-col-md-11{width:91.66666667%}.bk-bs-col-md-10{width:83.33333333%}.bk-bs-col-md-9{width:75%}.bk-bs-col-md-8{width:66.66666667%}.bk-bs-col-md-7{width:58.33333333%}.bk-bs-col-md-6{width:50%}.bk-bs-col-md-5{width:41.66666667%}.bk-bs-col-md-4{width:33.33333333%}.bk-bs-col-md-3{width:25%}.bk-bs-col-md-2{width:16.66666667%}.bk-bs-col-md-1{width:8.33333333%}.bk-bs-col-md-pull-12{right:100%}.bk-bs-col-md-pull-11{right:91.66666667%}.bk-bs-col-md-pull-10{right:83.33333333%}.bk-bs-col-md-pull-9{right:75%}.bk-bs-col-md-pull-8{right:66.66666667%}.bk-bs-col-md-pull-7{right:58.33333333%}.bk-bs-col-md-pull-6{right:50%}.bk-bs-col-md-pull-5{right:41.66666667%}.bk-bs-col-md-pull-4{right:33.33333333%}.bk-bs-col-md-pull-3{right:25%}.bk-bs-col-md-pull-2{right:16.66666667%}.bk-bs-col-md-pull-1{right:8.33333333%}.bk-bs-col-md-pull-0{right:0}.bk-bs-col-md-push-12{left:100%}.bk-bs-col-md-push-11{left:91.66666667%}.bk-bs-col-md-push-10{left:83.33333333%}.bk-bs-col-md-push-9{left:75%}.bk-bs-col-md-push-8{left:66.66666667%}.bk-bs-col-md-push-7{left:58.33333333%}.bk-bs-col-md-push-6{left:50%}.bk-bs-col-md-push-5{left:41.66666667%}.bk-bs-col-md-push-4{left:33.33333333%}.bk-bs-col-md-push-3{left:25%}.bk-bs-col-md-push-2{left:16.66666667%}.bk-bs-col-md-push-1{left:8.33333333%}.bk-bs-col-md-push-0{left:0}.bk-bs-col-md-offset-12{margin-left:100%}.bk-bs-col-md-offset-11{margin-left:91.66666667%}.bk-bs-col-md-offset-10{margin-left:83.33333333%}.bk-bs-col-md-offset-9{margin-left:75%}.bk-bs-col-md-offset-8{margin-left:66.66666667%}.bk-bs-col-md-offset-7{margin-left:58.33333333%}.bk-bs-col-md-offset-6{margin-left:50%}.bk-bs-col-md-offset-5{margin-left:41.66666667%}.bk-bs-col-md-offset-4{margin-left:33.33333333%}.bk-bs-col-md-offset-3{margin-left:25%}.bk-bs-col-md-offset-2{margin-left:16.66666667%}.bk-bs-col-md-offset-1{margin-left:8.33333333%}.bk-bs-col-md-offset-0{margin-left:0}}@media(min-width:1200px){.bk-bs-col-lg-1,.bk-bs-col-lg-2,.bk-bs-col-lg-3,.bk-bs-col-lg-4,.bk-bs-col-lg-5,.bk-bs-col-lg-6,.bk-bs-col-lg-7,.bk-bs-col-lg-8,.bk-bs-col-lg-9,.bk-bs-col-lg-10,.bk-bs-col-lg-11,.bk-bs-col-lg-12{float:left}.bk-bs-col-lg-12{width:100%}.bk-bs-col-lg-11{width:91.66666667%}.bk-bs-col-lg-10{width:83.33333333%}.bk-bs-col-lg-9{width:75%}.bk-bs-col-lg-8{width:66.66666667%}.bk-bs-col-lg-7{width:58.33333333%}.bk-bs-col-lg-6{width:50%}.bk-bs-col-lg-5{width:41.66666667%}.bk-bs-col-lg-4{width:33.33333333%}.bk-bs-col-lg-3{width:25%}.bk-bs-col-lg-2{width:16.66666667%}.bk-bs-col-lg-1{width:8.33333333%}.bk-bs-col-lg-pull-12{right:100%}.bk-bs-col-lg-pull-11{right:91.66666667%}.bk-bs-col-lg-pull-10{right:83.33333333%}.bk-bs-col-lg-pull-9{right:75%}.bk-bs-col-lg-pull-8{right:66.66666667%}.bk-bs-col-lg-pull-7{right:58.33333333%}.bk-bs-col-lg-pull-6{right:50%}.bk-bs-col-lg-pull-5{right:41.66666667%}.bk-bs-col-lg-pull-4{right:33.33333333%}.bk-bs-col-lg-pull-3{right:25%}.bk-bs-col-lg-pull-2{right:16.66666667%}.bk-bs-col-lg-pull-1{right:8.33333333%}.bk-bs-col-lg-pull-0{right:0}.bk-bs-col-lg-push-12{left:100%}.bk-bs-col-lg-push-11{left:91.66666667%}.bk-bs-col-lg-push-10{left:83.33333333%}.bk-bs-col-lg-push-9{left:75%}.bk-bs-col-lg-push-8{left:66.66666667%}.bk-bs-col-lg-push-7{left:58.33333333%}.bk-bs-col-lg-push-6{left:50%}.bk-bs-col-lg-push-5{left:41.66666667%}.bk-bs-col-lg-push-4{left:33.33333333%}.bk-bs-col-lg-push-3{left:25%}.bk-bs-col-lg-push-2{left:16.66666667%}.bk-bs-col-lg-push-1{left:8.33333333%}.bk-bs-col-lg-push-0{left:0}.bk-bs-col-lg-offset-12{margin-left:100%}.bk-bs-col-lg-offset-11{margin-left:91.66666667%}.bk-bs-col-lg-offset-10{margin-left:83.33333333%}.bk-bs-col-lg-offset-9{margin-left:75%}.bk-bs-col-lg-offset-8{margin-left:66.66666667%}.bk-bs-col-lg-offset-7{margin-left:58.33333333%}.bk-bs-col-lg-offset-6{margin-left:50%}.bk-bs-col-lg-offset-5{margin-left:41.66666667%}.bk-bs-col-lg-offset-4{margin-left:33.33333333%}.bk-bs-col-lg-offset-3{margin-left:25%}.bk-bs-col-lg-offset-2{margin-left:16.66666667%}.bk-bs-col-lg-offset-1{margin-left:8.33333333%}.bk-bs-col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.bk-bs-table{width:100%;margin-bottom:20px}.bk-bs-table>thead>tr>th,.bk-bs-table>tbody>tr>th,.bk-bs-table>tfoot>tr>th,.bk-bs-table>thead>tr>td,.bk-bs-table>tbody>tr>td,.bk-bs-table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.bk-bs-table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.bk-bs-table>caption+thead>tr:first-child>th,.bk-bs-table>colgroup+thead>tr:first-child>th,.bk-bs-table>thead:first-child>tr:first-child>th,.bk-bs-table>caption+thead>tr:first-child>td,.bk-bs-table>colgroup+thead>tr:first-child>td,.bk-bs-table>thead:first-child>tr:first-child>td{border-top:0}.bk-bs-table>tbody+tbody{border-top:2px solid #ddd}.bk-bs-table .bk-bs-table{background-color:#fff}.bk-bs-table-condensed>thead>tr>th,.bk-bs-table-condensed>tbody>tr>th,.bk-bs-table-condensed>tfoot>tr>th,.bk-bs-table-condensed>thead>tr>td,.bk-bs-table-condensed>tbody>tr>td,.bk-bs-table-condensed>tfoot>tr>td{padding:5px}.bk-bs-table-bordered{border:1px solid #ddd}.bk-bs-table-bordered>thead>tr>th,.bk-bs-table-bordered>tbody>tr>th,.bk-bs-table-bordered>tfoot>tr>th,.bk-bs-table-bordered>thead>tr>td,.bk-bs-table-bordered>tbody>tr>td,.bk-bs-table-bordered>tfoot>tr>td{border:1px solid #ddd}.bk-bs-table-bordered>thead>tr>th,.bk-bs-table-bordered>thead>tr>td{border-bottom-width:2px}.bk-bs-table-striped>tbody>tr:nth-child(odd)>td,.bk-bs-table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.bk-bs-table-hover>tbody>tr:hover>td,.bk-bs-table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.bk-bs-table>thead>tr>td.active,.bk-bs-table>tbody>tr>td.active,.bk-bs-table>tfoot>tr>td.active,.bk-bs-table>thead>tr>th.active,.bk-bs-table>tbody>tr>th.active,.bk-bs-table>tfoot>tr>th.active,.bk-bs-table>thead>tr.active>td,.bk-bs-table>tbody>tr.active>td,.bk-bs-table>tfoot>tr.active>td,.bk-bs-table>thead>tr.active>th,.bk-bs-table>tbody>tr.active>th,.bk-bs-table>tfoot>tr.active>th{background-color:#f5f5f5}.bk-bs-table-hover>tbody>tr>td.active:hover,.bk-bs-table-hover>tbody>tr>th.active:hover,.bk-bs-table-hover>tbody>tr.active:hover>td,.bk-bs-table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.bk-bs-table>thead>tr>td.success,.bk-bs-table>tbody>tr>td.success,.bk-bs-table>tfoot>tr>td.success,.bk-bs-table>thead>tr>th.success,.bk-bs-table>tbody>tr>th.success,.bk-bs-table>tfoot>tr>th.success,.bk-bs-table>thead>tr.success>td,.bk-bs-table>tbody>tr.success>td,.bk-bs-table>tfoot>tr.success>td,.bk-bs-table>thead>tr.success>th,.bk-bs-table>tbody>tr.success>th,.bk-bs-table>tfoot>tr.success>th{background-color:#dff0d8}.bk-bs-table-hover>tbody>tr>td.success:hover,.bk-bs-table-hover>tbody>tr>th.success:hover,.bk-bs-table-hover>tbody>tr.success:hover>td,.bk-bs-table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.bk-bs-table>thead>tr>td.info,.bk-bs-table>tbody>tr>td.info,.bk-bs-table>tfoot>tr>td.info,.bk-bs-table>thead>tr>th.info,.bk-bs-table>tbody>tr>th.info,.bk-bs-table>tfoot>tr>th.info,.bk-bs-table>thead>tr.info>td,.bk-bs-table>tbody>tr.info>td,.bk-bs-table>tfoot>tr.info>td,.bk-bs-table>thead>tr.info>th,.bk-bs-table>tbody>tr.info>th,.bk-bs-table>tfoot>tr.info>th{background-color:#d9edf7}.bk-bs-table-hover>tbody>tr>td.info:hover,.bk-bs-table-hover>tbody>tr>th.info:hover,.bk-bs-table-hover>tbody>tr.info:hover>td,.bk-bs-table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.bk-bs-table>thead>tr>td.warning,.bk-bs-table>tbody>tr>td.warning,.bk-bs-table>tfoot>tr>td.warning,.bk-bs-table>thead>tr>th.warning,.bk-bs-table>tbody>tr>th.warning,.bk-bs-table>tfoot>tr>th.warning,.bk-bs-table>thead>tr.warning>td,.bk-bs-table>tbody>tr.warning>td,.bk-bs-table>tfoot>tr.warning>td,.bk-bs-table>thead>tr.warning>th,.bk-bs-table>tbody>tr.warning>th,.bk-bs-table>tfoot>tr.warning>th{background-color:#fcf8e3}.bk-bs-table-hover>tbody>tr>td.warning:hover,.bk-bs-table-hover>tbody>tr>th.warning:hover,.bk-bs-table-hover>tbody>tr.warning:hover>td,.bk-bs-table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.bk-bs-table>thead>tr>td.danger,.bk-bs-table>tbody>tr>td.danger,.bk-bs-table>tfoot>tr>td.danger,.bk-bs-table>thead>tr>th.danger,.bk-bs-table>tbody>tr>th.danger,.bk-bs-table>tfoot>tr>th.danger,.bk-bs-table>thead>tr.danger>td,.bk-bs-table>tbody>tr.danger>td,.bk-bs-table>tfoot>tr.danger>td,.bk-bs-table>thead>tr.danger>th,.bk-bs-table>tbody>tr.danger>th,.bk-bs-table>tfoot>tr.danger>th{background-color:#f2dede}.bk-bs-table-hover>tbody>tr>td.danger:hover,.bk-bs-table-hover>tbody>tr>th.danger:hover,.bk-bs-table-hover>tbody>tr.danger:hover>td,.bk-bs-table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media(max-width:767px){.bk-bs-table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.bk-bs-table-responsive>.bk-bs-table{margin-bottom:0}.bk-bs-table-responsive>.bk-bs-table>thead>tr>th,.bk-bs-table-responsive>.bk-bs-table>tbody>tr>th,.bk-bs-table-responsive>.bk-bs-table>tfoot>tr>th,.bk-bs-table-responsive>.bk-bs-table>thead>tr>td,.bk-bs-table-responsive>.bk-bs-table>tbody>tr>td,.bk-bs-table-responsive>.bk-bs-table>tfoot>tr>td{white-space:nowrap}.bk-bs-table-responsive>.bk-bs-table-bordered{border:0}.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr>th:first-child,.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr>th:first-child,.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr>th:first-child,.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr>td:first-child,.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr>td:first-child,.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr>td:first-child{border-left:0}.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr>th:last-child,.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr>th:last-child,.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr>th:last-child,.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr>td:last-child,.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr>td:last-child,.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr>td:last-child{border-right:0}.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr:last-child>th,.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr:last-child>th,.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr:last-child>td,.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.bk-bs-form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bk-bs-form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,0.6)}.bk-bs-form-control::-moz-placeholder{color:#999;opacity:1}.bk-bs-form-control:-ms-input-placeholder{color:#999}.bk-bs-form-control::-webkit-input-placeholder{color:#999}.bk-bs-form-control[disabled],.bk-bs-form-control[readonly],fieldset[disabled] .bk-bs-form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.bk-bs-form-control{height:auto}input[type="search"]{-webkit-appearance:none}input[type="date"]{line-height:34px}.bk-bs-form-group{margin-bottom:15px}.bk-bs-radio,.bk-bs-checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.bk-bs-radio label,.bk-bs-checkbox label{display:inline;font-weight:normal;cursor:pointer}.bk-bs-radio input[type="radio"],.bk-bs-radio-inline input[type="radio"],.bk-bs-checkbox input[type="checkbox"],.bk-bs-checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.bk-bs-radio+.bk-bs-radio,.bk-bs-checkbox+.bk-bs-checkbox{margin-top:-5px}.bk-bs-radio-inline,.bk-bs-checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.bk-bs-radio-inline+.bk-bs-radio-inline,.bk-bs-checkbox-inline+.bk-bs-checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],.bk-bs-radio[disabled],.bk-bs-radio-inline[disabled],.bk-bs-checkbox[disabled],.bk-bs-checkbox-inline[disabled],fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"],fieldset[disabled] .bk-bs-radio,fieldset[disabled] .bk-bs-radio-inline,fieldset[disabled] .bk-bs-checkbox,fieldset[disabled] .bk-bs-checkbox-inline{cursor:not-allowed}.bk-bs-input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.bk-bs-input-sm{height:30px;line-height:30px}textarea.bk-bs-input-sm,select[multiple].bk-bs-input-sm{height:auto}.bk-bs-input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.bk-bs-input-lg{height:46px;line-height:46px}textarea.bk-bs-input-lg,select[multiple].bk-bs-input-lg{height:auto}.bk-bs-has-feedback{position:relative}.bk-bs-has-feedback .bk-bs-form-control{padding-right:42.5px}.bk-bs-has-feedback .bk-bs-form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.bk-bs-has-success .bk-bs-help-block,.bk-bs-has-success .bk-bs-control-label,.bk-bs-has-success .bk-bs-radio,.bk-bs-has-success .bk-bs-checkbox,.bk-bs-has-success .bk-bs-radio-inline,.bk-bs-has-success .bk-bs-checkbox-inline{color:#3c763d}.bk-bs-has-success .bk-bs-form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.bk-bs-has-success .bk-bs-form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.bk-bs-has-success .bk-bs-input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.bk-bs-has-success .bk-bs-form-control-feedback{color:#3c763d}.bk-bs-has-warning .bk-bs-help-block,.bk-bs-has-warning .bk-bs-control-label,.bk-bs-has-warning .bk-bs-radio,.bk-bs-has-warning .bk-bs-checkbox,.bk-bs-has-warning .bk-bs-radio-inline,.bk-bs-has-warning .bk-bs-checkbox-inline{color:#8a6d3b}.bk-bs-has-warning .bk-bs-form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.bk-bs-has-warning .bk-bs-form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.bk-bs-has-warning .bk-bs-input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.bk-bs-has-warning .bk-bs-form-control-feedback{color:#8a6d3b}.bk-bs-has-error .bk-bs-help-block,.bk-bs-has-error .bk-bs-control-label,.bk-bs-has-error .bk-bs-radio,.bk-bs-has-error .bk-bs-checkbox,.bk-bs-has-error .bk-bs-radio-inline,.bk-bs-has-error .bk-bs-checkbox-inline{color:#a94442}.bk-bs-has-error .bk-bs-form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.bk-bs-has-error .bk-bs-form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.bk-bs-has-error .bk-bs-input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.bk-bs-has-error .bk-bs-form-control-feedback{color:#a94442}.bk-bs-form-control-static{margin-bottom:0}.bk-bs-help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media(min-width:768px){.bk-bs-form-inline .bk-bs-form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.bk-bs-form-inline .bk-bs-form-control{display:inline-block;width:auto;vertical-align:middle}.bk-bs-form-inline .bk-bs-input-group>.bk-bs-form-control{width:100%}.bk-bs-form-inline .bk-bs-control-label{margin-bottom:0;vertical-align:middle}.bk-bs-form-inline .bk-bs-radio,.bk-bs-form-inline .bk-bs-checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.bk-bs-form-inline .bk-bs-radio input[type="radio"],.bk-bs-form-inline .bk-bs-checkbox input[type="checkbox"]{float:none;margin-left:0}.bk-bs-form-inline .bk-bs-has-feedback .bk-bs-form-control-feedback{top:0}}.bk-bs-form-horizontal .bk-bs-control-label,.bk-bs-form-horizontal .bk-bs-radio,.bk-bs-form-horizontal .bk-bs-checkbox,.bk-bs-form-horizontal .bk-bs-radio-inline,.bk-bs-form-horizontal .bk-bs-checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.bk-bs-form-horizontal .bk-bs-radio,.bk-bs-form-horizontal .bk-bs-checkbox{min-height:27px}.bk-bs-form-horizontal .bk-bs-form-group{margin-left:-15px;margin-right:-15px}.bk-bs-form-horizontal .bk-bs-form-control-static{padding-top:7px}@media(min-width:768px){.bk-bs-form-horizontal .bk-bs-control-label{text-align:right}}.bk-bs-form-horizontal .bk-bs-has-feedback .bk-bs-form-control-feedback{top:0;right:15px}.bk-bs-btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bk-bs-btn:focus,.bk-bs-btn:active:focus,.bk-bs-btn.bk-bs-active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.bk-bs-btn:hover,.bk-bs-btn:focus{color:#333;text-decoration:none}.bk-bs-btn:active,.bk-bs-btn.bk-bs-active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.bk-bs-btn.bk-bs-disabled,.bk-bs-btn[disabled],fieldset[disabled] .bk-bs-btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.bk-bs-btn-default{color:#333;background-color:#fff;border-color:#ccc}.bk-bs-btn-default:hover,.bk-bs-btn-default:focus,.bk-bs-btn-default:active,.bk-bs-btn-default.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.bk-bs-btn-default:active,.bk-bs-btn-default.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-default{background-image:none}.bk-bs-btn-default.bk-bs-disabled,.bk-bs-btn-default[disabled],fieldset[disabled] .bk-bs-btn-default,.bk-bs-btn-default.bk-bs-disabled:hover,.bk-bs-btn-default[disabled]:hover,fieldset[disabled] .bk-bs-btn-default:hover,.bk-bs-btn-default.bk-bs-disabled:focus,.bk-bs-btn-default[disabled]:focus,fieldset[disabled] .bk-bs-btn-default:focus,.bk-bs-btn-default.bk-bs-disabled:active,.bk-bs-btn-default[disabled]:active,fieldset[disabled] .bk-bs-btn-default:active,.bk-bs-btn-default.bk-bs-disabled.bk-bs-active,.bk-bs-btn-default[disabled].bk-bs-active,fieldset[disabled] .bk-bs-btn-default.bk-bs-active{background-color:#fff;border-color:#ccc}.bk-bs-btn-default .bk-bs-badge{color:#fff;background-color:#333}.bk-bs-btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.bk-bs-btn-primary:hover,.bk-bs-btn-primary:focus,.bk-bs-btn-primary:active,.bk-bs-btn-primary.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.bk-bs-btn-primary:active,.bk-bs-btn-primary.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-primary{background-image:none}.bk-bs-btn-primary.bk-bs-disabled,.bk-bs-btn-primary[disabled],fieldset[disabled] .bk-bs-btn-primary,.bk-bs-btn-primary.bk-bs-disabled:hover,.bk-bs-btn-primary[disabled]:hover,fieldset[disabled] .bk-bs-btn-primary:hover,.bk-bs-btn-primary.bk-bs-disabled:focus,.bk-bs-btn-primary[disabled]:focus,fieldset[disabled] .bk-bs-btn-primary:focus,.bk-bs-btn-primary.bk-bs-disabled:active,.bk-bs-btn-primary[disabled]:active,fieldset[disabled] .bk-bs-btn-primary:active,.bk-bs-btn-primary.bk-bs-disabled.bk-bs-active,.bk-bs-btn-primary[disabled].bk-bs-active,fieldset[disabled] .bk-bs-btn-primary.bk-bs-active{background-color:#428bca;border-color:#357ebd}.bk-bs-btn-primary .bk-bs-badge{color:#428bca;background-color:#fff}.bk-bs-btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.bk-bs-btn-success:hover,.bk-bs-btn-success:focus,.bk-bs-btn-success:active,.bk-bs-btn-success.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-success{color:#fff;background-color:#47a447;border-color:#398439}.bk-bs-btn-success:active,.bk-bs-btn-success.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-success{background-image:none}.bk-bs-btn-success.bk-bs-disabled,.bk-bs-btn-success[disabled],fieldset[disabled] .bk-bs-btn-success,.bk-bs-btn-success.bk-bs-disabled:hover,.bk-bs-btn-success[disabled]:hover,fieldset[disabled] .bk-bs-btn-success:hover,.bk-bs-btn-success.bk-bs-disabled:focus,.bk-bs-btn-success[disabled]:focus,fieldset[disabled] .bk-bs-btn-success:focus,.bk-bs-btn-success.bk-bs-disabled:active,.bk-bs-btn-success[disabled]:active,fieldset[disabled] .bk-bs-btn-success:active,.bk-bs-btn-success.bk-bs-disabled.bk-bs-active,.bk-bs-btn-success[disabled].bk-bs-active,fieldset[disabled] .bk-bs-btn-success.bk-bs-active{background-color:#5cb85c;border-color:#4cae4c}.bk-bs-btn-success .bk-bs-badge{color:#5cb85c;background-color:#fff}.bk-bs-btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.bk-bs-btn-info:hover,.bk-bs-btn-info:focus,.bk-bs-btn-info:active,.bk-bs-btn-info.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.bk-bs-btn-info:active,.bk-bs-btn-info.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-info{background-image:none}.bk-bs-btn-info.bk-bs-disabled,.bk-bs-btn-info[disabled],fieldset[disabled] .bk-bs-btn-info,.bk-bs-btn-info.bk-bs-disabled:hover,.bk-bs-btn-info[disabled]:hover,fieldset[disabled] .bk-bs-btn-info:hover,.bk-bs-btn-info.bk-bs-disabled:focus,.bk-bs-btn-info[disabled]:focus,fieldset[disabled] .bk-bs-btn-info:focus,.bk-bs-btn-info.bk-bs-disabled:active,.bk-bs-btn-info[disabled]:active,fieldset[disabled] .bk-bs-btn-info:active,.bk-bs-btn-info.bk-bs-disabled.bk-bs-active,.bk-bs-btn-info[disabled].bk-bs-active,fieldset[disabled] .bk-bs-btn-info.bk-bs-active{background-color:#5bc0de;border-color:#46b8da}.bk-bs-btn-info .bk-bs-badge{color:#5bc0de;background-color:#fff}.bk-bs-btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.bk-bs-btn-warning:hover,.bk-bs-btn-warning:focus,.bk-bs-btn-warning:active,.bk-bs-btn-warning.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.bk-bs-btn-warning:active,.bk-bs-btn-warning.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-warning{background-image:none}.bk-bs-btn-warning.bk-bs-disabled,.bk-bs-btn-warning[disabled],fieldset[disabled] .bk-bs-btn-warning,.bk-bs-btn-warning.bk-bs-disabled:hover,.bk-bs-btn-warning[disabled]:hover,fieldset[disabled] .bk-bs-btn-warning:hover,.bk-bs-btn-warning.bk-bs-disabled:focus,.bk-bs-btn-warning[disabled]:focus,fieldset[disabled] .bk-bs-btn-warning:focus,.bk-bs-btn-warning.bk-bs-disabled:active,.bk-bs-btn-warning[disabled]:active,fieldset[disabled] .bk-bs-btn-warning:active,.bk-bs-btn-warning.bk-bs-disabled.bk-bs-active,.bk-bs-btn-warning[disabled].bk-bs-active,fieldset[disabled] .bk-bs-btn-warning.bk-bs-active{background-color:#f0ad4e;border-color:#eea236}.bk-bs-btn-warning .bk-bs-badge{color:#f0ad4e;background-color:#fff}.bk-bs-btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.bk-bs-btn-danger:hover,.bk-bs-btn-danger:focus,.bk-bs-btn-danger:active,.bk-bs-btn-danger.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.bk-bs-btn-danger:active,.bk-bs-btn-danger.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-danger{background-image:none}.bk-bs-btn-danger.bk-bs-disabled,.bk-bs-btn-danger[disabled],fieldset[disabled] .bk-bs-btn-danger,.bk-bs-btn-danger.bk-bs-disabled:hover,.bk-bs-btn-danger[disabled]:hover,fieldset[disabled] .bk-bs-btn-danger:hover,.bk-bs-btn-danger.bk-bs-disabled:focus,.bk-bs-btn-danger[disabled]:focus,fieldset[disabled] .bk-bs-btn-danger:focus,.bk-bs-btn-danger.bk-bs-disabled:active,.bk-bs-btn-danger[disabled]:active,fieldset[disabled] .bk-bs-btn-danger:active,.bk-bs-btn-danger.bk-bs-disabled.bk-bs-active,.bk-bs-btn-danger[disabled].bk-bs-active,fieldset[disabled] .bk-bs-btn-danger.bk-bs-active{background-color:#d9534f;border-color:#d43f3a}.bk-bs-btn-danger .bk-bs-badge{color:#d9534f;background-color:#fff}.bk-bs-btn-link{color:#428bca;font-weight:normal;cursor:pointer;border-radius:0}.bk-bs-btn-link,.bk-bs-btn-link:active,.bk-bs-btn-link[disabled],fieldset[disabled] .bk-bs-btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.bk-bs-btn-link,.bk-bs-btn-link:hover,.bk-bs-btn-link:focus,.bk-bs-btn-link:active{border-color:transparent}.bk-bs-btn-link:hover,.bk-bs-btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.bk-bs-btn-link[disabled]:hover,fieldset[disabled] .bk-bs-btn-link:hover,.bk-bs-btn-link[disabled]:focus,fieldset[disabled] .bk-bs-btn-link:focus{color:#999;text-decoration:none}.bk-bs-btn-lg,.bk-bs-btn-group-lg>.bk-bs-btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.bk-bs-btn-sm,.bk-bs-btn-group-sm>.bk-bs-btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.bk-bs-btn-xs,.bk-bs-btn-group-xs>.bk-bs-btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.bk-bs-btn-block{display:block;width:100%;padding-left:0;padding-right:0}.bk-bs-btn-block+.bk-bs-btn-block{margin-top:5px}input[type="submit"].bk-bs-btn-block,input[type="reset"].bk-bs-btn-block,input[type="button"].bk-bs-btn-block{width:100%}.bk-bs-caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.bk-bs-dropdown{position:relative}.bk-bs-dropdown-toggle:focus{outline:0}.bk-bs-dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.bk-bs-dropdown-menu.bk-bs-pull-right{right:0;left:auto}.bk-bs-dropdown-menu .bk-bs-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.bk-bs-dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.bk-bs-dropdown-menu>li>a:hover,.bk-bs-dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.bk-bs-dropdown-menu>.bk-bs-active>a,.bk-bs-dropdown-menu>.bk-bs-active>a:hover,.bk-bs-dropdown-menu>.bk-bs-active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.bk-bs-dropdown-menu>.bk-bs-disabled>a,.bk-bs-dropdown-menu>.bk-bs-disabled>a:hover,.bk-bs-dropdown-menu>.bk-bs-disabled>a:focus{color:#999}.bk-bs-dropdown-menu>.bk-bs-disabled>a:hover,.bk-bs-dropdown-menu>.bk-bs-disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.bk-bs-Microsoft.bk-bs-gradient(enabled = false);cursor:not-allowed}.bk-bs-open>.bk-bs-dropdown-menu{display:block}.bk-bs-open>a{outline:0}.bk-bs-dropdown-menu-right{left:auto;right:0}.bk-bs-dropdown-menu-left{left:0;right:auto}.bk-bs-dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.bk-bs-dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.bk-bs-pull-right>.bk-bs-dropdown-menu{right:0;left:auto}.bk-bs-dropup .bk-bs-caret,.bk-bs-navbar-fixed-bottom .bk-bs-dropdown .bk-bs-caret{border-top:0;border-bottom:4px solid;content:""}.bk-bs-dropup .bk-bs-dropdown-menu,.bk-bs-navbar-fixed-bottom .bk-bs-dropdown .bk-bs-dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media(min-width:768px){.bk-bs-navbar-right .bk-bs-dropdown-menu{left:auto;right:0}.bk-bs-navbar-right .bk-bs-dropdown-menu-left{left:0;right:auto}}.bk-bs-btn-group,.bk-bs-btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.bk-bs-btn-group>.bk-bs-btn,.bk-bs-btn-group-vertical>.bk-bs-btn{position:relative;float:left}.bk-bs-btn-group>.bk-bs-btn:hover,.bk-bs-btn-group-vertical>.bk-bs-btn:hover,.bk-bs-btn-group>.bk-bs-btn:focus,.bk-bs-btn-group-vertical>.bk-bs-btn:focus,.bk-bs-btn-group>.bk-bs-btn:active,.bk-bs-btn-group-vertical>.bk-bs-btn:active,.bk-bs-btn-group>.bk-bs-btn.bk-bs-active,.bk-bs-btn-group-vertical>.bk-bs-btn.bk-bs-active{z-index:2}.bk-bs-btn-group>.bk-bs-btn:focus,.bk-bs-btn-group-vertical>.bk-bs-btn:focus{outline:0}.bk-bs-btn-group .bk-bs-btn+.bk-bs-btn,.bk-bs-btn-group .bk-bs-btn+.bk-bs-btn-group,.bk-bs-btn-group .bk-bs-btn-group+.bk-bs-btn,.bk-bs-btn-group .bk-bs-btn-group+.bk-bs-btn-group{margin-left:-1px}.bk-bs-btn-toolbar{margin-left:-5px}.bk-bs-btn-toolbar .bk-bs-btn-group,.bk-bs-btn-toolbar .bk-bs-input-group{float:left}.bk-bs-btn-toolbar>.bk-bs-btn,.bk-bs-btn-toolbar>.bk-bs-btn-group,.bk-bs-btn-toolbar>.bk-bs-input-group{margin-left:5px}.bk-bs-btn-group>.bk-bs-btn:not(:first-child):not(:last-child):not(.bk-bs-dropdown-toggle){border-radius:0}.bk-bs-btn-group>.bk-bs-btn:first-child{margin-left:0}.bk-bs-btn-group>.bk-bs-btn:first-child:not(:last-child):not(.bk-bs-dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.bk-bs-btn-group>.bk-bs-btn:last-child:not(:first-child),.bk-bs-btn-group>.bk-bs-dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.bk-bs-btn-group>.bk-bs-btn-group{float:left}.bk-bs-btn-group>.bk-bs-btn-group:not(:first-child):not(:last-child)>.bk-bs-btn{border-radius:0}.bk-bs-btn-group>.bk-bs-btn-group:first-child>.bk-bs-btn:last-child,.bk-bs-btn-group>.bk-bs-btn-group:first-child>.bk-bs-dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.bk-bs-btn-group>.bk-bs-btn-group:last-child>.bk-bs-btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.bk-bs-btn-group .bk-bs-dropdown-toggle:active,.bk-bs-btn-group.bk-bs-open .bk-bs-dropdown-toggle{outline:0}.bk-bs-btn-group>.bk-bs-btn+.bk-bs-dropdown-toggle{padding-left:8px;padding-right:8px}.bk-bs-btn-group>.bk-bs-btn-lg+.bk-bs-dropdown-toggle{padding-left:12px;padding-right:12px}.bk-bs-btn-group.bk-bs-open .bk-bs-dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.bk-bs-btn-group.bk-bs-open .bk-bs-dropdown-toggle.bk-bs-btn-link{-webkit-box-shadow:none;box-shadow:none}.bk-bs-btn .bk-bs-caret{margin-left:0}.bk-bs-btn-lg .bk-bs-caret{border-width:5px 5px 0;border-bottom-width:0}.bk-bs-dropup .bk-bs-btn-lg .bk-bs-caret{border-width:0 5px 5px}.bk-bs-btn-group-vertical>.bk-bs-btn,.bk-bs-btn-group-vertical>.bk-bs-btn-group,.bk-bs-btn-group-vertical>.bk-bs-btn-group>.bk-bs-btn{display:block;float:none;width:100%;max-width:100%}.bk-bs-btn-group-vertical>.bk-bs-btn-group>.bk-bs-btn{float:none}.bk-bs-btn-group-vertical>.bk-bs-btn+.bk-bs-btn,.bk-bs-btn-group-vertical>.bk-bs-btn+.bk-bs-btn-group,.bk-bs-btn-group-vertical>.bk-bs-btn-group+.bk-bs-btn,.bk-bs-btn-group-vertical>.bk-bs-btn-group+.bk-bs-btn-group{margin-top:-1px;margin-left:0}.bk-bs-btn-group-vertical>.bk-bs-btn:not(:first-child):not(:last-child){border-radius:0}.bk-bs-btn-group-vertical>.bk-bs-btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.bk-bs-btn-group-vertical>.bk-bs-btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.bk-bs-btn-group-vertical>.bk-bs-btn-group:not(:first-child):not(:last-child)>.bk-bs-btn{border-radius:0}.bk-bs-btn-group-vertical>.bk-bs-btn-group:first-child:not(:last-child)>.bk-bs-btn:last-child,.bk-bs-btn-group-vertical>.bk-bs-btn-group:first-child:not(:last-child)>.bk-bs-dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.bk-bs-btn-group-vertical>.bk-bs-btn-group:last-child:not(:first-child)>.bk-bs-btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.bk-bs-btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.bk-bs-btn-group-justified>.bk-bs-btn,.bk-bs-btn-group-justified>.bk-bs-btn-group{float:none;display:table-cell;width:1%}.bk-bs-btn-group-justified>.bk-bs-btn-group .bk-bs-btn{width:100%}[data-bk-bs-toggle="buttons"]>.bk-bs-btn>input[type="radio"],[data-bk-bs-toggle="buttons"]>.bk-bs-btn>input[type="checkbox"]{display:none}.bk-bs-input-group{position:relative;display:table;border-collapse:separate}.bk-bs-input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.bk-bs-input-group .bk-bs-form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.bk-bs-input-group-lg>.bk-bs-form-control,.bk-bs-input-group-lg>.bk-bs-input-group-addon,.bk-bs-input-group-lg>.bk-bs-input-group-btn>.bk-bs-btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.bk-bs-input-group-lg>.bk-bs-form-control,select.bk-bs-input-group-lg>.bk-bs-input-group-addon,select.bk-bs-input-group-lg>.bk-bs-input-group-btn>.bk-bs-btn{height:46px;line-height:46px}textarea.bk-bs-input-group-lg>.bk-bs-form-control,textarea.bk-bs-input-group-lg>.bk-bs-input-group-addon,textarea.bk-bs-input-group-lg>.bk-bs-input-group-btn>.bk-bs-btn,select[multiple].bk-bs-input-group-lg>.bk-bs-form-control,select[multiple].bk-bs-input-group-lg>.bk-bs-input-group-addon,select[multiple].bk-bs-input-group-lg>.bk-bs-input-group-btn>.bk-bs-btn{height:auto}.bk-bs-input-group-sm>.bk-bs-form-control,.bk-bs-input-group-sm>.bk-bs-input-group-addon,.bk-bs-input-group-sm>.bk-bs-input-group-btn>.bk-bs-btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.bk-bs-input-group-sm>.bk-bs-form-control,select.bk-bs-input-group-sm>.bk-bs-input-group-addon,select.bk-bs-input-group-sm>.bk-bs-input-group-btn>.bk-bs-btn{height:30px;line-height:30px}textarea.bk-bs-input-group-sm>.bk-bs-form-control,textarea.bk-bs-input-group-sm>.bk-bs-input-group-addon,textarea.bk-bs-input-group-sm>.bk-bs-input-group-btn>.bk-bs-btn,select[multiple].bk-bs-input-group-sm>.bk-bs-form-control,select[multiple].bk-bs-input-group-sm>.bk-bs-input-group-addon,select[multiple].bk-bs-input-group-sm>.bk-bs-input-group-btn>.bk-bs-btn{height:auto}.bk-bs-input-group-addon,.bk-bs-input-group-btn,.bk-bs-input-group .bk-bs-form-control{display:table-cell}.bk-bs-input-group-addon:not(:first-child):not(:last-child),.bk-bs-input-group-btn:not(:first-child):not(:last-child),.bk-bs-input-group .bk-bs-form-control:not(:first-child):not(:last-child){border-radius:0}.bk-bs-input-group-addon,.bk-bs-input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.bk-bs-input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.bk-bs-input-group-addon.bk-bs-input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.bk-bs-input-group-addon.bk-bs-input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.bk-bs-input-group-addon input[type="radio"],.bk-bs-input-group-addon input[type="checkbox"]{margin-top:0}.bk-bs-input-group .bk-bs-form-control:first-child,.bk-bs-input-group-addon:first-child,.bk-bs-input-group-btn:first-child>.bk-bs-btn,.bk-bs-input-group-btn:first-child>.bk-bs-btn-group>.bk-bs-btn,.bk-bs-input-group-btn:first-child>.bk-bs-dropdown-toggle,.bk-bs-input-group-btn:last-child>.bk-bs-btn:not(:last-child):not(.bk-bs-dropdown-toggle),.bk-bs-input-group-btn:last-child>.bk-bs-btn-group:not(:last-child)>.bk-bs-btn{border-bottom-right-radius:0;border-top-right-radius:0}.bk-bs-input-group-addon:first-child{border-right:0}.bk-bs-input-group .bk-bs-form-control:last-child,.bk-bs-input-group-addon:last-child,.bk-bs-input-group-btn:last-child>.bk-bs-btn,.bk-bs-input-group-btn:last-child>.bk-bs-btn-group>.bk-bs-btn,.bk-bs-input-group-btn:last-child>.bk-bs-dropdown-toggle,.bk-bs-input-group-btn:first-child>.bk-bs-btn:not(:first-child),.bk-bs-input-group-btn:first-child>.bk-bs-btn-group:not(:first-child)>.bk-bs-btn{border-bottom-left-radius:0;border-top-left-radius:0}.bk-bs-input-group-addon:last-child{border-left:0}.bk-bs-input-group-btn{position:relative;font-size:0;white-space:nowrap}.bk-bs-input-group-btn>.bk-bs-btn{position:relative}.bk-bs-input-group-btn>.bk-bs-btn+.bk-bs-btn{margin-left:-1px}.bk-bs-input-group-btn>.bk-bs-btn:hover,.bk-bs-input-group-btn>.bk-bs-btn:focus,.bk-bs-input-group-btn>.bk-bs-btn:active{z-index:2}.bk-bs-input-group-btn:first-child>.bk-bs-btn,.bk-bs-input-group-btn:first-child>.bk-bs-btn-group{margin-right:-1px}.bk-bs-input-group-btn:last-child>.bk-bs-btn,.bk-bs-input-group-btn:last-child>.bk-bs-btn-group{margin-left:-1px}.bk-bs-nav{margin-bottom:0;padding-left:0;list-style:none}.bk-bs-nav>li{position:relative;display:block}.bk-bs-nav>li>a{position:relative;display:block;padding:10px 15px}.bk-bs-nav>li>a:hover,.bk-bs-nav>li>a:focus{text-decoration:none;background-color:#eee}.bk-bs-nav>li.bk-bs-disabled>a{color:#999}.bk-bs-nav>li.bk-bs-disabled>a:hover,.bk-bs-nav>li.bk-bs-disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.bk-bs-nav .bk-bs-open>a,.bk-bs-nav .bk-bs-open>a:hover,.bk-bs-nav .bk-bs-open>a:focus{background-color:#eee;border-color:#428bca}.bk-bs-nav .bk-bs-nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.bk-bs-nav>li>a>img{max-width:none}.bk-bs-nav-tabs{border-bottom:1px solid #ddd}.bk-bs-nav-tabs>li{float:left;margin-bottom:-1px}.bk-bs-nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.bk-bs-nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.bk-bs-nav-tabs>li.bk-bs-active>a,.bk-bs-nav-tabs>li.bk-bs-active>a:hover,.bk-bs-nav-tabs>li.bk-bs-active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.bk-bs-nav-tabs.bk-bs-nav-justified{width:100%;border-bottom:0}.bk-bs-nav-tabs.bk-bs-nav-justified>li{float:none}.bk-bs-nav-tabs.bk-bs-nav-justified>li>a{text-align:center;margin-bottom:5px}.bk-bs-nav-tabs.bk-bs-nav-justified>.bk-bs-dropdown .bk-bs-dropdown-menu{top:auto;left:auto}@media(min-width:768px){.bk-bs-nav-tabs.bk-bs-nav-justified>li{display:table-cell;width:1%}.bk-bs-nav-tabs.bk-bs-nav-justified>li>a{margin-bottom:0}}.bk-bs-nav-tabs.bk-bs-nav-justified>li>a{margin-right:0;border-radius:4px}.bk-bs-nav-tabs.bk-bs-nav-justified>.bk-bs-active>a,.bk-bs-nav-tabs.bk-bs-nav-justified>.bk-bs-active>a:hover,.bk-bs-nav-tabs.bk-bs-nav-justified>.bk-bs-active>a:focus{border:1px solid #ddd}@media(min-width:768px){.bk-bs-nav-tabs.bk-bs-nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.bk-bs-nav-tabs.bk-bs-nav-justified>.bk-bs-active>a,.bk-bs-nav-tabs.bk-bs-nav-justified>.bk-bs-active>a:hover,.bk-bs-nav-tabs.bk-bs-nav-justified>.bk-bs-active>a:focus{border-bottom-color:#fff}}.bk-bs-nav-pills>li{float:left}.bk-bs-nav-pills>li>a{border-radius:4px}.bk-bs-nav-pills>li+li{margin-left:2px}.bk-bs-nav-pills>li.bk-bs-active>a,.bk-bs-nav-pills>li.bk-bs-active>a:hover,.bk-bs-nav-pills>li.bk-bs-active>a:focus{color:#fff;background-color:#428bca}.bk-bs-nav-stacked>li{float:none}.bk-bs-nav-stacked>li+li{margin-top:2px;margin-left:0}.bk-bs-nav-justified{width:100%}.bk-bs-nav-justified>li{float:none}.bk-bs-nav-justified>li>a{text-align:center;margin-bottom:5px}.bk-bs-nav-justified>.bk-bs-dropdown .bk-bs-dropdown-menu{top:auto;left:auto}@media(min-width:768px){.bk-bs-nav-justified>li{display:table-cell;width:1%}.bk-bs-nav-justified>li>a{margin-bottom:0}}.bk-bs-nav-tabs-justified{border-bottom:0}.bk-bs-nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.bk-bs-nav-tabs-justified>.bk-bs-active>a,.bk-bs-nav-tabs-justified>.bk-bs-active>a:hover,.bk-bs-nav-tabs-justified>.bk-bs-active>a:focus{border:1px solid #ddd}@media(min-width:768px){.bk-bs-nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.bk-bs-nav-tabs-justified>.bk-bs-active>a,.bk-bs-nav-tabs-justified>.bk-bs-active>a:hover,.bk-bs-nav-tabs-justified>.bk-bs-active>a:focus{border-bottom-color:#fff}}.bk-bs-tab-content>.bk-bs-tab-pane{display:none}.bk-bs-tab-content>.bk-bs-active{display:block}.bk-bs-nav-tabs .bk-bs-dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.bk-bs-label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.bk-bs-label[href]:hover,.bk-bs-label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.bk-bs-label:empty{display:none}.bk-bs-btn .bk-bs-label{position:relative;top:-1px}.bk-bs-label-default{background-color:#999}.bk-bs-label-default[href]:hover,.bk-bs-label-default[href]:focus{background-color:gray}.bk-bs-label-primary{background-color:#428bca}.bk-bs-label-primary[href]:hover,.bk-bs-label-primary[href]:focus{background-color:#3071a9}.bk-bs-label-success{background-color:#5cb85c}.bk-bs-label-success[href]:hover,.bk-bs-label-success[href]:focus{background-color:#449d44}.bk-bs-label-info{background-color:#5bc0de}.bk-bs-label-info[href]:hover,.bk-bs-label-info[href]:focus{background-color:#31b0d5}.bk-bs-label-warning{background-color:#f0ad4e}.bk-bs-label-warning[href]:hover,.bk-bs-label-warning[href]:focus{background-color:#ec971f}.bk-bs-label-danger{background-color:#d9534f}.bk-bs-label-danger[href]:hover,.bk-bs-label-danger[href]:focus{background-color:#c9302c}.bk-bs-panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.bk-bs-panel-body{padding:15px}.bk-bs-panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.bk-bs-panel-heading>.bk-bs-dropdown .bk-bs-dropdown-toggle{color:inherit}.bk-bs-panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.bk-bs-panel-title>a{color:inherit}.bk-bs-panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.bk-bs-panel>.bk-bs-list-group{margin-bottom:0}.bk-bs-panel>.bk-bs-list-group .bk-bs-list-group-item{border-width:1px 0;border-radius:0}.bk-bs-panel>.bk-bs-list-group:first-child .bk-bs-list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.bk-bs-panel>.bk-bs-list-group:last-child .bk-bs-list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.bk-bs-panel-heading+.bk-bs-list-group .bk-bs-list-group-item:first-child{border-top-width:0}.bk-bs-panel>.bk-bs-table,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table{margin-bottom:0}.bk-bs-panel>.bk-bs-table:first-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.bk-bs-panel>.bk-bs-table:first-child>thead:first-child>tr:first-child td:first-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child>thead:first-child>tr:first-child td:first-child,.bk-bs-panel>.bk-bs-table:first-child>tbody:first-child>tr:first-child td:first-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child>tbody:first-child>tr:first-child td:first-child,.bk-bs-panel>.bk-bs-table:first-child>thead:first-child>tr:first-child th:first-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child>thead:first-child>tr:first-child th:first-child,.bk-bs-panel>.bk-bs-table:first-child>tbody:first-child>tr:first-child th:first-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.bk-bs-panel>.bk-bs-table:first-child>thead:first-child>tr:first-child td:last-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child>thead:first-child>tr:first-child td:last-child,.bk-bs-panel>.bk-bs-table:first-child>tbody:first-child>tr:first-child td:last-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child>tbody:first-child>tr:first-child td:last-child,.bk-bs-panel>.bk-bs-table:first-child>thead:first-child>tr:first-child th:last-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child>thead:first-child>tr:first-child th:last-child,.bk-bs-panel>.bk-bs-table:first-child>tbody:first-child>tr:first-child th:last-child,.bk-bs-panel>.bk-bs-table-responsive:first-child>.bk-bs-table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.bk-bs-panel>.bk-bs-table:last-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.bk-bs-panel>.bk-bs-table:last-child>tbody:last-child>tr:last-child td:first-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child>tbody:last-child>tr:last-child td:first-child,.bk-bs-panel>.bk-bs-table:last-child>tfoot:last-child>tr:last-child td:first-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child>tfoot:last-child>tr:last-child td:first-child,.bk-bs-panel>.bk-bs-table:last-child>tbody:last-child>tr:last-child th:first-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child>tbody:last-child>tr:last-child th:first-child,.bk-bs-panel>.bk-bs-table:last-child>tfoot:last-child>tr:last-child th:first-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.bk-bs-panel>.bk-bs-table:last-child>tbody:last-child>tr:last-child td:last-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child>tbody:last-child>tr:last-child td:last-child,.bk-bs-panel>.bk-bs-table:last-child>tfoot:last-child>tr:last-child td:last-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child>tfoot:last-child>tr:last-child td:last-child,.bk-bs-panel>.bk-bs-table:last-child>tbody:last-child>tr:last-child th:last-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child>tbody:last-child>tr:last-child th:last-child,.bk-bs-panel>.bk-bs-table:last-child>tfoot:last-child>tr:last-child th:last-child,.bk-bs-panel>.bk-bs-table-responsive:last-child>.bk-bs-table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.bk-bs-panel>.bk-bs-panel-body+.bk-bs-table,.bk-bs-panel>.bk-bs-panel-body+.bk-bs-table-responsive{border-top:1px solid #ddd}.bk-bs-panel>.bk-bs-table>tbody:first-child>tr:first-child th,.bk-bs-panel>.bk-bs-table>tbody:first-child>tr:first-child td{border-top:0}.bk-bs-panel>.bk-bs-table-bordered,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered{border:0}.bk-bs-panel>.bk-bs-table-bordered>thead>tr>th:first-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr>th:first-child,.bk-bs-panel>.bk-bs-table-bordered>tbody>tr>th:first-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr>th:first-child,.bk-bs-panel>.bk-bs-table-bordered>tfoot>tr>th:first-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr>th:first-child,.bk-bs-panel>.bk-bs-table-bordered>thead>tr>td:first-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr>td:first-child,.bk-bs-panel>.bk-bs-table-bordered>tbody>tr>td:first-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr>td:first-child,.bk-bs-panel>.bk-bs-table-bordered>tfoot>tr>td:first-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr>td:first-child{border-left:0}.bk-bs-panel>.bk-bs-table-bordered>thead>tr>th:last-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr>th:last-child,.bk-bs-panel>.bk-bs-table-bordered>tbody>tr>th:last-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr>th:last-child,.bk-bs-panel>.bk-bs-table-bordered>tfoot>tr>th:last-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr>th:last-child,.bk-bs-panel>.bk-bs-table-bordered>thead>tr>td:last-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr>td:last-child,.bk-bs-panel>.bk-bs-table-bordered>tbody>tr>td:last-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr>td:last-child,.bk-bs-panel>.bk-bs-table-bordered>tfoot>tr>td:last-child,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr>td:last-child{border-right:0}.bk-bs-panel>.bk-bs-table-bordered>thead>tr:first-child>td,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr:first-child>td,.bk-bs-panel>.bk-bs-table-bordered>tbody>tr:first-child>td,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr:first-child>td,.bk-bs-panel>.bk-bs-table-bordered>thead>tr:first-child>th,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>thead>tr:first-child>th,.bk-bs-panel>.bk-bs-table-bordered>tbody>tr:first-child>th,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr:first-child>th{border-bottom:0}.bk-bs-panel>.bk-bs-table-bordered>tbody>tr:last-child>td,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr:last-child>td,.bk-bs-panel>.bk-bs-table-bordered>tfoot>tr:last-child>td,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr:last-child>td,.bk-bs-panel>.bk-bs-table-bordered>tbody>tr:last-child>th,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tbody>tr:last-child>th,.bk-bs-panel>.bk-bs-table-bordered>tfoot>tr:last-child>th,.bk-bs-panel>.bk-bs-table-responsive>.bk-bs-table-bordered>tfoot>tr:last-child>th{border-bottom:0}.bk-bs-panel>.bk-bs-table-responsive{border:0;margin-bottom:0}.bk-bs-panel-group{margin-bottom:20px}.bk-bs-panel-group .bk-bs-panel{margin-bottom:0;border-radius:4px;overflow:hidden}.bk-bs-panel-group .bk-bs-panel+.bk-bs-panel{margin-top:5px}.bk-bs-panel-group .bk-bs-panel-heading{border-bottom:0}.bk-bs-panel-group .bk-bs-panel-heading+.bk-bs-panel-collapse .bk-bs-panel-body{border-top:1px solid #ddd}.bk-bs-panel-group .bk-bs-panel-footer{border-top:0}.bk-bs-panel-group .bk-bs-panel-footer+.bk-bs-panel-collapse .bk-bs-panel-body{border-bottom:1px solid #ddd}.bk-bs-panel-default{border-color:#ddd}.bk-bs-panel-default>.bk-bs-panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.bk-bs-panel-default>.bk-bs-panel-heading+.bk-bs-panel-collapse .bk-bs-panel-body{border-top-color:#ddd}.bk-bs-panel-default>.bk-bs-panel-footer+.bk-bs-panel-collapse .bk-bs-panel-body{border-bottom-color:#ddd}.bk-bs-panel-primary{border-color:#428bca}.bk-bs-panel-primary>.bk-bs-panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.bk-bs-panel-primary>.bk-bs-panel-heading+.bk-bs-panel-collapse .bk-bs-panel-body{border-top-color:#428bca}.bk-bs-panel-primary>.bk-bs-panel-footer+.bk-bs-panel-collapse .bk-bs-panel-body{border-bottom-color:#428bca}.bk-bs-panel-success{border-color:#d6e9c6}.bk-bs-panel-success>.bk-bs-panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.bk-bs-panel-success>.bk-bs-panel-heading+.bk-bs-panel-collapse .bk-bs-panel-body{border-top-color:#d6e9c6}.bk-bs-panel-success>.bk-bs-panel-footer+.bk-bs-panel-collapse .bk-bs-panel-body{border-bottom-color:#d6e9c6}.bk-bs-panel-info{border-color:#bce8f1}.bk-bs-panel-info>.bk-bs-panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.bk-bs-panel-info>.bk-bs-panel-heading+.bk-bs-panel-collapse .bk-bs-panel-body{border-top-color:#bce8f1}.bk-bs-panel-info>.bk-bs-panel-footer+.bk-bs-panel-collapse .bk-bs-panel-body{border-bottom-color:#bce8f1}.bk-bs-panel-warning{border-color:#faebcc}.bk-bs-panel-warning>.bk-bs-panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.bk-bs-panel-warning>.bk-bs-panel-heading+.bk-bs-panel-collapse .bk-bs-panel-body{border-top-color:#faebcc}.bk-bs-panel-warning>.bk-bs-panel-footer+.bk-bs-panel-collapse .bk-bs-panel-body{border-bottom-color:#faebcc}.bk-bs-panel-danger{border-color:#ebccd1}.bk-bs-panel-danger>.bk-bs-panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.bk-bs-panel-danger>.bk-bs-panel-heading+.bk-bs-panel-collapse .bk-bs-panel-body{border-top-color:#ebccd1}.bk-bs-panel-danger>.bk-bs-panel-footer+.bk-bs-panel-collapse .bk-bs-panel-body{border-bottom-color:#ebccd1}.bk-bs-close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.bk-bs-close:hover,.bk-bs-close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.bk-bs-close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.bk-bs-modal-open{overflow:hidden}.bk-bs-modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.bk-bs-modal.bk-bs-fade .bk-bs-modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.bk-bs-modal.bk-bs-in .bk-bs-modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.bk-bs-modal-dialog{position:relative;width:auto;margin:10px}.bk-bs-modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:0}.bk-bs-modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.bk-bs-modal-backdrop.bk-bs-fade{opacity:0;filter:alpha(opacity=0)}.bk-bs-modal-backdrop.bk-bs-in{opacity:.5;filter:alpha(opacity=50)}.bk-bs-modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.bk-bs-modal-header .bk-bs-close{margin-top:-2px}.bk-bs-modal-title{margin:0;line-height:1.42857143}.bk-bs-modal-body{position:relative;padding:20px}.bk-bs-modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.bk-bs-modal-footer .bk-bs-btn+.bk-bs-btn{margin-left:5px;margin-bottom:0}.bk-bs-modal-footer .bk-bs-btn-group .bk-bs-btn+.bk-bs-btn{margin-left:-1px}.bk-bs-modal-footer .bk-bs-btn-block+.bk-bs-btn-block{margin-left:0}@media(min-width:768px){.bk-bs-modal-dialog{width:600px;margin:30px auto}.bk-bs-modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.bk-bs-modal-sm{width:300px}}@media(min-width:992px){.bk-bs-modal-lg{width:900px}}.bk-bs-clearfix:before,.bk-bs-clearfix:after,.bk-bs-container:before,.bk-bs-container:after,.bk-bs-container-fluid:before,.bk-bs-container-fluid:after,.bk-bs-row:before,.bk-bs-row:after,.bk-bs-form-horizontal .bk-bs-form-group:before,.bk-bs-form-horizontal .bk-bs-form-group:after,.bk-bs-btn-toolbar:before,.bk-bs-btn-toolbar:after,.bk-bs-btn-group-vertical>.bk-bs-btn-group:before,.bk-bs-btn-group-vertical>.bk-bs-btn-group:after,.bk-bs-nav:before,.bk-bs-nav:after,.bk-bs-panel-body:before,.bk-bs-panel-body:after,.bk-bs-modal-footer:before,.bk-bs-modal-footer:after{content:" ";display:table}.bk-bs-clearfix:after,.bk-bs-container:after,.bk-bs-container-fluid:after,.bk-bs-row:after,.bk-bs-form-horizontal .bk-bs-form-group:after,.bk-bs-btn-toolbar:after,.bk-bs-btn-group-vertical>.bk-bs-btn-group:after,.bk-bs-nav:after,.bk-bs-panel-body:after,.bk-bs-modal-footer:after{clear:both}.bk-bs-center-block{display:block;margin-left:auto;margin-right:auto}.bk-bs-pull-right{float:right !important}.bk-bs-pull-left{float:left !important}.bk-bs-hide{display:none !important}.bk-bs-show{display:block !important}.bk-bs-invisible{visibility:hidden}.bk-bs-text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.bk-bs-hidden{display:none !important;visibility:hidden !important}.bk-bs-affix{position:fixed}.tableelem{padding:2px 10px;border:2px white;background-color:#e0e0e0}.tableheader{background-color:silver}#notebook .bk-plot-wrapper table{border:none !important}#notebook .bk-plot-wrapper table tr{border:none !important}#notebook .bk-plot-wrapper table tr td{border:none !important;padding:0 !important;margin:0 !important}#notebook .bk-plot-wrapper table tr td.bk-plot-above{border-bottom:2px solid #efefef !important}#notebook .bk-plot-wrapper table tr td.bk-plot-below{border-top:2px solid #efefef !important}#notebook .bk-plot-wrapper table tr td.bk-plot-left{border-right:2px solid #efefef !important}#notebook .bk-plot-wrapper table tr td.bk-plot-right{border-left:2px solid #efefef !important}.bk-table table tr td{padding:2px}.bk-table form table tr td{padding:2px}.bk-table form table tr td input{padding:0}.jsp:after,.bk-plot:after,.bk-canvas-wrapper:after,.bk-sidebar:after,.bk-box:after{content:" ";height:0;display:block;clear:both}.bk-canvas-wrapper .bk-resize-popup{position:absolute;left:0;top:0;width:40px;height:40px;overflow:hidden;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEnSURBVEiJzZXBioQwDIb/XQuF6U3wCRSEvv8zeFOoB2++QD14aqHSOntYtthxdHesDptTk4Z+hKR/PqqquuNi+7wa8DYIWTplWSJN0yDBGAMhBJxzhyFBJY8AACCErGKv2u4L1lp0XRdVBfBLTwghuN1uUYBNiDHGn4uiQJZl50GmaYJSCm3bou/700BBT4QQAL57IaUEAOR57kEAMAxDHMRaG1wuQc45aK1fBqwgz+wHpJSCUuoayBJ01P6/djHG/jR1hzWDMQbOuZedvak7XAljzAMe/xGlFEmSeP9wJVv/SGsNzjmcc2iaJg6yBbLWghASqHf0dEkpAwl6thpOGWGl1O46iIZQSsE5Dxp9OsQ5h3meV/FxHP05erdaa1HX9W7OW2TlC31ceRWbb5+AAAAAAElFTkSuQmCC);background-position:bottom right;background-repeat:no-repeat;cursor:se-resize}.bk-canvas-wrapper:hover .bk-resize-popup{display:block}.bk-sidebar.bk-logo{margin:5px auto}.bk-logo{position:relative;display:block;background-repeat:no-repeat}.bk-logo.grey{filter:url("data:image/svg+xml;utf8,<svgxmlns=\'http://www.w3.org/2000/svg\'><filterid=\'grayscale\'><feColorMatrixtype=\'matrix\'values=\'0.33330.33330.3333000.33330.33330.3333000.33330.33330.33330000010\'/></filter></svg>#grayscale");filter:gray;-webkit-filter:grayscale(100%)}.bk-logo-notebook{display:inline-block;vertical-align:middle;margin-right:5px}.bk-banner>span{display:inline-block;vertical-align:middle}.bk-logo-small{width:20px;height:20px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAOkSURBVDiNjZRtaJVlGMd/1/08zzln5zjP1LWcU9N0NkN8m2CYjpgQYQXqSs0I84OLIC0hkEKoPtiH3gmKoiJDU7QpLgoLjLIQCpEsNJ1vqUOdO7ppbuec5+V+rj4ctwzd8IIbbi6u+8f1539dt3A78eXC7QizUF7gyV1fD1Yqg4JWz84yffhm0qkFqBogB9rM8tZdtwVsPUhWhGcFJngGeWrPzHm5oaMmkfEg1usvLFyc8jLRqDOMru7AyC8saQr7GG7f5fvDeH7Ej8CM66nIF+8yngt6HWaKh7k49Soy9nXurCi1o3qUbS3zWfrYeQDTB/Qj6kX6Ybhw4B+bOYoLKCC9H3Nu/leUTZ1JdRWkkn2ldcCamzrcf47KKXdAJllSlxAOkRgyHsGC/zRday5Qld9DyoM4/q/rUoy/CXh3jzOu3bHUVZeU+DEn8FInkPBFlu3+nW3Nw0mk6vCDiWg8CeJaxEwuHS3+z5RgY+YBR6V1Z1nxSOfoaPa4LASWxxdNp+VWTk7+4vzaou8v8PN+xo+KY2xsw6une2frhw05CTYOmQvsEhjhWjn0bmXPjpE1+kplmmkP3suftwTubK9Vq22qKmrBhpY4jvd5afdRA3wGjFAgcnTK2s4hY0/GPNIb0nErGMCRxWOOX64Z8RAC4oCXdklmEvcL8o0BfkNK4lUg9HTl+oPlQxdNo3Mg4Nv175e/1LDGzZen30MEjRUtmXSfiTVu1kK8W4txyV6BMKlbgk3lMwYCiusNy9fVfvvwMxv8Ynl6vxoByANLTWplvuj/nF9m2+PDtt1eiHPBr1oIfhCChQMBw6Aw0UulqTKZdfVvfG7VcfIqLG9bcldL/+pdWTLxLUy8Qq38heUIjh4XlzZxzQm19lLFlr8vdQ97rjZVOLf8nclzckbcD4wxXMidpX30sFd37Fv/GtwwhzhxGVAprjbg0gCAEeIgwCZyTV2Z1REEW8O4py0wsjeloKoMr6iCY6dP92H6Vw/oTyICIthibxjm/DfN9lVz8IqtqKYLUXfoKVMVQVVJOElGjrnnUt9T9wbgp8AyYKaGlqingHZU/uG2NTZSVqwHQTWkx9hxjkpWDaCg6Ckj5qebgBVbT3V3NNXMSiWSDdGV3hrtzla7J+duwPOToIg42ChPQOQjspnSlp1V+Gjdged7+8UN5CRAV7a5EdFNwCjEaBR27b3W890TE7g24NAP/mMDXRWrGoFPQI9ls/MWO2dWFAar/xcOIImbbpA3zgAAAABJRU5ErkJggg==)}.bk-logo-medium{width:35px;height:35px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAf9SURBVFiFvZh7cFTVHcc/59y7793sJiFAwkvAYDRqFWwdraLVlj61diRYsDjqCFbFKrYo0CltlSq1tLaC2GprGIriGwqjFu10OlrGv8RiK/IICYECSWBDkt3s695zTv9IAtlHeOn0O7Mzu797z+/3Ob/z+p0VfBq9doNFljuABwAXw2PcvGHt6bgwxhz7Ls4YZNVXxxANLENwE2D1W9PAGmAhszZ0/X9gll5yCbHoOirLzmaQs0F6F8QMZq1v/8xgNm7DYwwjgXJLYL4witQ16+sv/U9HdDmV4WrKw6B06cZC/RMrM4MZ7xz61DAbtzEXmAvUAX4pMOVecg9/MFFu3j3Gz7gQBLygS2RGumBkL0cubiFRsR3LzVBV1UMk3IrW73PT9C2lYOwhQB4ClhX1AuKpjLcV27oEjyUpNUJCg1CvcejykWTCXyQgzic2HIIBjg3pS6+uRLKAhumZvD4U+tq0jTrgkVKQQtLekfTtxIPAkhTNF6G7kZm7aPp6M9myKVQEoaYaIhEQYvD781DML/RfBGNZXAl4irJiwBa07e/y7cQnBaJghIX6ENl2GR/fGCBoz6cm5qeyEqQA5ZYA5x5eeiV0Qph4gjFAUSwAr6QllQgcxS/Jm25Cr2Tmpsk03XI9NfI31FTZBEOgVOk51adqDBNPCNPSRlkiDXbBEwOU2WxH+I7itQZ62g56OjM33suq1YsZHVtGZSUI2QdyYgkgOthQNIF7BIGDnRAJgJSgj69cUx1gB8PkOGwL4E1gPrM27gIg7NlGKLQApc7BmEnAxP5g/rw4YqBrCDB5xHkw5rdR/1qTrN/hKNo6YUwVDNpFsnjYS8RbidBPcPXFP6R6yfExuOXmN4A3jv1+8ZUwgY9D2OWjUZE6lO88jDwHI8ZixGiMKSeYTBamCoDk6kDAb6y1OcH1a6KpD/fZesoFw5FlIXAVCIiH4PxrV+p2npVDToTBmtjY8t1swh2V61E9KqWiyuPEjM8dbfxuvfa49Zayf9R136Wr8mBSf/T7bNteA8zwaGEUbFpckWwq95n59dUIywKl2fbOIS5e8bWSu0tJ1a5redAYfqkdjesodFajcgaVNWhXo1C9SrkN3Usmv3UMJrc6/DDwkwEntkEJLe67tSLhvyzK8rHDQWleve5CGk4VZEB1r+5bg2E2si+Y0QatDK6jUVkX5eg2YYlp++ZM+rfMNYamAj8Y7MAVWFqaR1f/t2xzU4IHjybBtthzuiAASqv7jTF7jOqDMAakFHgDNsFyP+FhwZHBmH9F7cutIYkQCylYYv1AZSqsn1/+bX51OMMjPSl2nAnM7hnjOx2v53YgNWAzHM9Q/9l0lQWPSCBSyokAtOBC1Rj+w/1Xs+STDp4/E5g7Rs2zm2+oeVd7PUuHKDf6A4r5EsPT5K3gfCnBXNUYnvGzb+KcCczYYWOnLpy4eOXuG2oec0PBN8XQQAnpvS35AvAykr56rWhPBiV4MvtceGLxk5Mr6A1O8IfK7rl7xJ0r9kyumuP4fa0lMqTBLJIAJqEf1J3qE92lMBndlyfRD2YBghHC4hlny7ASqCeWo5zaoDdIWfnIefNGTb9fC73QDfhyBUCNOxrGPSUBfPem9us253YTV+3mcBbdkUYfzmHiLqZbYdIGHHON2ZlemXouaJUOO6TqtdHEQuXYY8Yt+EbDgmlS6RdzkaDTv2P9A3gICiq93sWhb5mc5wVhuU3Y7m5hOc3So7qFT3SLgOXHb/cyOfMn7xROegoC/PTcn3v8gbKPgDopJFk3R/uBPWQiwQ+2/GJevRMObLUzqe/saJjQUQTTftEVMW9tWxPgAocwcj9abNcZe7s+6t2R2xXZG7zyYLp8Q1PiRBBHym5bYuXi8Qt+/LvGu9f/5YDAxABsaRNPH6Xr4D4Sk87a897SOy9v/fKwjoF2eQel95yDESGEF6gEMwKhLwKus3wOVjTtes7qzgLdXTMnNCNoEpbcrtNuq6N7Xh/+eqcbj94xQkp7mdKpW5XbtbR8Z26kgMCAf2UU5YEovRUVRHbu2b3vK1UdDFkDCyMRQxbpdv8nhKAGIa7QaQedzT07fFPny53R738JoVYBdVrnsNx9XZ9v33UeGO+AA2MMUkgqQ5UcdDLZSFeVgONnXeHqSAC5Ew1BXwko0D1Zct3dT1duOjS3MzZnEUJtBuoQAq3SGOLR4ekjn9NC5nVOaYXf9lETrUkmOJy3pOz8OKIb2A1cWhJCCEzOxU2mUPror+2/L3yyM3pkM7jTjr1nBOgkGeyQ7erxpdJsMAS9wb2F9rzMxNY1K2PMU0WtZV82VU8Wp6vbKJVo9Lx/+4cydORdxCCQ/kDGTZCWsRpLu7VD7bfKqL8V2orKTp/PtzaXy42jr6TwAuisi+7JolUG4wY+8vyrISCMtRrLKWpvjAOqx/QGhp0rjRo5xD3x98CWQuOQN8qumRMmI7jKZPUEpzNVZsj4Zbaq1to5tZZsKIydLWojhIXrJnES79EaOzv3du2NytKuxzJKAA6wF8xqEE8s2jo/1wd/khslQGxd81Zg62Bbp31XBH+iETt7Y3ELA0iU6iGDlQ5mexe0VEx4a3x8V1AaYwFJgTiwaOsDmeK2J8nMUOqsnB1A+dcA04ucCYt0urkjmflk9iT2v30q/gZn5rQPvor4n9Ou634PeBzoznes/iot/7WnClKoM/+zCIjH5kwT8ChQjTHPIPTjFV3PpU/Hx+DM/A9U3IXI4SPCYAAAAABJRU5ErkJggg==)}.bk-logo-large{width:75px;height:75px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAYAAAA4TnrqAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAABNHSURBVHiczZx5nFxVlce/576q6uqq6q7e0t0habIRgQScfEBAJ4MLo4gogY9CAkkIApElCqOCI8IAKriMg6MwoqiBgERMIJEECCoIKKIYWcImS9KEJCxJOr2kt1rfu2f+eN2d7qS7tu4Efp9Pf7rqvvvOOfV759577r3nPuG9hF/PmQXmZEQ/CkwEtqLyV8Q+yPz7nn03TFLVgc/ybhiwD+469WiQLwOfAmqHqbEb1bWo/JCFa148kKa9t8hafuqVGLkWCBVQuxvVy1mw9hf726x+vDfIOqMxzCdm3U4sPK/oe1V/wIK1X98PVg2j6t0m63uzx2O95YyLn0BlFKwtRcrPmb/morE2bW8MJsvsb2X74PsfmoToAwScEwgFAM17ywi4kLtO+78xtCwvDixZPzjuGMQ8CRxFwIFgALRksgC+xF2n/WyMrMuLMWmG9zxLRQjGIYxjGFc5tJLsOX8+/Ij1LbX/g2gDVqE8BI3VYEdFVj9uYf6ai8dC0N4Y3AwDpQpZs4GZYjgVmCNwEBAFIsPV3ZXFa4wmo2QDEMr6hcGSVe8L8S7il0sitM66l2Ci1S/UXqz2kEgYKmKdTJray+lzukejpmiLVz9NNBjgOoRLASdffQGs0tOTDbyO0WkDF8rGiCwnk6FnQged0xcRTC4aKPcdopfaWkNNdRtepouVq5pRfQbVFZw1d2Oxqorqs377DNXBIL9H+AoFENVvc9CwvTVV9iLSN+oZ6euvijV3L5hsM11Nt9F+pIs6DAi0FkSgujpK/bhygsGJwAxgDiLfwpiXWLnqTn5z96Si1BVacd0GAo7DauDfilEAINDd3FnxGkb9Dj3ggFMQ1yPDZO6h9cjraX//SYidgFhftrVQXg4NDVAV90nbdxAJAgsx5klWrPpowSoLregKF4vwsULrD4Zj2NKbLOtA1H/4AQeMKXUk9Ah2XcimT19BYuJ1ONnJgE+SMVBTA/XjIFzml+XWMR7ht6y458hCFBdE1r0bKBfhskLqDocywyuo+LpUIRQAp6SBeBOBnuP5+9V3E3bvx7hNA94UiUBjA8QrfW8qPNCtRmQZd63MO90qiCwHPgYU1b4HI2R4hUxwC4r/9EuJr1RvoXzzUTx30RYan38O0RkD3lTb503BYCHeNByOxphT8lUqrBkaPl2s9sEYF2ETGccftgUIOsV07i2ozmfB2ot5+gaoeesh0ElDvKmy0q85qgBXFuarURBZIkwZhRWp2nq2EvKCoOAU41n6R6x+hAVrf8MNP6qi+uUHsfYIHGcsvGkohKNZsbw8V5W8ZH34mwQzLvVSeqy/E2gj4AZR/P4qv7As8A3a3E+ycO2r/PDGCpQ/YPV4YrE93qQ6epL2oAYNTc9VIW9kOLOJirdaiU+q952iBNteF8Hlu24YFEJBvymOLOdFVJewYO0TACy5KkA2u5RQ6FiqqiAW9WuVtlKRC2WINOSqkNezGuLYzl681i4/liwWqmz1NXm9gBJ0GHlKqrcAsweIOuPcIJNqfkUsNpfGBqiIjbU3DUYAqMlXISdCIeJA3a5OqIxAtAy8Ih6qgL8M7Ho7CQVdAk5wnx+r+g7IpSxYu3qg7MabY4isJBY9mVjMLxt7b9obORXk9SwRykWIWgs7OsDT4pYqFPw5mKcOASME9oncH/Y78TWrh5QKh1MVL6eiYiOqqf3kTUUhr2dpX3dsDHQloLUTGqvALewhewov+Z8sOI7imP5lmTToNSTlBs6/b19pl37xKeAElq+qxrF1iEwGZoAciXA40AjUAZUFWTIGyEvWG0HKJmcJO/h9VstuiIUhEs7fKlR5Wyy7AD9iLws6fReeBZawYO36vBYuPL0D6AA2AQ8PlC//dTmB0DRgKiJTQN8HchjooSCNFDjRLwZ5yTq/I3Tc47EMGfG1uxa2d8DUhpHmqHsgwitzjiLha3KEsqBB9Zdk7OWce3/XqCxfuCCJ77UvDZQtXSZURCpQUw8cBnIEwmHAwYP+giMLzd3Wc5KVWhaZH0o5N0UwyTWxlDUQdQx0JWFXFzRUg+flUN3fXwGEAq9izGc4a826XDpHhcXnKtDV99cMPDDk+jNX19E2oZHuimNxAx9AZCb+wmUVSB0BN5NL/IhkpZZFFojIHR7qHJkKJjyrq1dVpY+PWaYYgZZOqCz3V4e9kZ/HawOfvvbEDmC/EHXQT1+emOnNzPLSWqtWRVURI2qMGHHMeFQCoiYrv5OsqHFVbJ2KdYCXbTDb7oYyh9hAts64gY92wX0j6RmWrNSyyJlGzJ2AKJAVjcxKBz+yrce7fkPEvb5MaHA92L4bJtePGGMqyhtjQ0duROJl4Ug8fKebtlVu2sXLeHiuxctarKeIKCoW6V/pGDSciw0QSgZADYj9cu33n9vYdsWsW4bTs0/okFoWWdBP1ODyrGHSnN7yU7Ien3LhZcdAZy+0dvmR/TDoPlBkNS+Y1uyEnEvClSFi4yLEJ1RQNaGCqgkx4uOjRGvKCUWCmIAgBtQq1lPUKqqKiqLGQ0UF5KfV39swZzg9QwhJLYvMM2LuIkf8FczKFytmdq+8so1/oEw1BqaPh7LQPqPj5myGmZ87htRYEFIIDl259XaEc6Av3hEZcHtVnxwvY3EzLm7a4mU9vIz/fwiEXuvZE3f/11F/G3ZHOn179GhB/gLknHkDGsw4x5w+uXPnB9I86lmmxyMwpWHorpYqT5w6i+NH+fuLwrTlr9cEQoH1wCEj1RERMH0bKZ5irWJdi5tyyaY9vIyHdS3W1R29HcmTkv993PP99xqA5K3hqKgsIz9RAJINeatXvVjX3g3HO8ITnYl9m6MIL5T6o0vF6wuntVu155Njmq6qqOc3QwQcxxAMByivClPZGKW6qZKqCRXED4o1jp9R99tD79l2UP+9BsAxznkIBa1D92FStib1s++ewk5VTjPCUzt3QzLjR/q+VRS91TQW2DRvyuOKfqegyoOap1qfRLWKE3QIRYOUxUJTQX/aX92kbi2vQuS8oq0SFqWXRRddeSptBj6RdvndO+0MPFPlwHtWP1Kkvqmqfyn1/sEECnLqoXdvXQhgjGOOB95filARuTF1W2z6FXPo3LaLOZ29rGjtBseQQXmrVGNHi21zD/Osp58Hdo+FPBG57JCVWyoMyL9SeoJIlRGWJW6NOUu/gHv1HSxo6+bB7iTJujitY2FoqWieP2Wzqn51jMTNckTOMKo0jUqMMNsxXAvAvdi3Wjm9tZPzurpIjoWVo8HGeZOXKXrnGImbJ+llsZtFWDIqMUpW0U+Undv75zEybMww7e7N1QGcZ2BUmy4Au4zAk6O2SAiKyM2p2yoO2NpSLnx1+lUnXdB0aRPA63OndljPfp48q6AFYJxR1T8B20YpCGCmiD2gmXgjoSZas7giXPH4/PrzDgfYdNaUx1X1+tFJ1S2m7LzetzTHTLsYiMiizO2x+WMhq1R8+7DrTNAJ1MXL45MrwhX3LWq8cBpAWtPXKTxRqlyFJ/rzD24CesfEWuVHqdvKp46JrBIQCEerrdpJZaEyqiPVh4QCwYcumHjJkVvPPNRV9EL8ta5i4aGy3ACUndu7yaq9CsEPIkaTPCnUG+Ms7bmtesyXdQs0oB5ostZSFa0iWhadaoXHvtB0yfGb5k5+2ap+pViJCus8Yx4ZiK/C5yZu1KS9R3usvx/cT1z/n1AMiR8LSuaA5KnvDRXbRN/6uyDUxGpwjKkVZM1F4y/60KZ5k29TkbuLEJlVa7/VfPpEd0gwmlqX+LzX4v7R25HFbs9iW1x0t4cmFXXVH0+MFESeiHw7fXvsgK469On9YP9nq5ZwIEx1eTWK1miw7KEl9ReeuvGMg+epSHMh8hS9fOO8yc/CXpF7/H4S6tqFePYFTSnaY7HtHnaAvCy21UW7PDRpwWMPaYM90Icjyi8SS8MHNJwQlSH5CopSFamiPFSOVRvzwuFV/xmcd+LbH2yai0g2j7ibXjtj0k39X/aZ5kSvd3ci+jkMu4YQ4OF7WKeLbfWwLS7eO1mfyA4P7bJoSsFlMHmHOcHATXvr2K8Qjhj8VVUREWqiNRg/ny7Q2djw4JKDP9vYW199lY6Qk6Dw89fmTvqPwWXDzgnLr3GbrbWfVHQXjgWjezzHkT13eYqmFN3tYXe52B1ZvB1ZvO1ZbKuH9lhI23NSS6N5c5/GAtf8y3fiDHOqzKqlPFhOdaQaqxZEnJ7xB635zJnfkGyk/A9qhtKg6E82zp20z1GXEXd3ot90N/ReFb5I02W/wrFtUpZ9XkLuDsAiqhjdLtCDNSG1EkQlgJIm472JxdFeD/XJDWrUvD56KvIj7JS/D6gf7pqqEo/ESWQSJLNJCARCgXTmO8f+4Pa7/vG1c94MpDNNxvUAfr5x7uRLhpORc9+w9+EjHg5N6ehEqEKZoWg9Vl5Q2KiJsiczOyu2jn96/bu2FNMSO3OWZ3sC4xMPPA0gKk0IZcPVVRQjhppYDdt3b0dR3HAoMO6lTXNnLF+3c9PnPo4XcH7W/NmJI86Tc5IVnLIrjHEEqESoFJiG0eMEoDJFeWWK3YdMfRNhm8JWVDcBL4C+qso7mUymu/Hed3Jsw5aO9qoLL7Re7w3JTMs3gacBVPRQyTFE9zfHqkgV7T3tiBGy5eHwwX96elKoJ3H/vfd/MeeCQu7teykguhKagCaB2Xsy+sSCdoTD4X/uPmtKM8g/VfUtYKOqt6Vm5baSF+VaK89vMMb8WOBMqxlct71i0OWcmXvge1g8EieRTpByUxgx2GCApseeeizfvWN4gGYIjIjUAh8G+TD07apAj0igveOsqTtR3Qg8L8Ir1upWYGfNyi0tuYS2Vy4+ESO3AFMQg+t1ASoA35v1Y+MnheSGquKIQ02shh2dO1AUQUhVV7bTk/ve/UXWSIgBMYGDETkGWABgjICwbffcKW96vdk7ate99cvBN7VULg4GhOsR+RoDni5Ym0Dpa+WiFcBhhRhh1RItixIvj9OeaMcRBwrg4kCTNTyMoGnvYLc3Hde099TgS23xxYeLyE+AE/aUCqoWz0tC3xaJhzvBIZAzzXEwrPpzx0QmQdpNI8g7ec3Mc33/Hgvu6+O8rjSZtsTr6fbEcfWPtjzXf7ktvvhiEfk7Q4jym7S1CaxNIQSMX2ZmFaNaVXGMQ22sFhFB0bz9aD7PSgBjP5r1ba1rxsXrzOBl3Dc81z1p4vrOZoC2qsUHifK/iIxw2NzB2hTWpkCc8SgIganFLoZaa4mEIsTL49rS1ZJ30yZnheoVW7sZo+2kAYiAgtedJtuWxGay3cDZBz3R4RNVef6JgvxpZKIAFGsT+ORI2JfLzFLMsWqpjlRnmmoO7shXt4CcUn1VkJIMGYI+b7IpF68ng2ZcUDoQTmt4bNdf34wsCkWCoWtF5Mr8wiyu1wsYHPUyAKo6odQ+w4jpriyL5T3lWsh+4SMl2jDYGt+butK47UmfKJGUBjKfqX+s9fG2qsVHRUNljxZGlN/feF4XIDh42VPmrI+P5siMopvTmeSOvD8jryTLOqC0s8VC30jnkm1L4HWn+z3Mw0kvaPhj59/a4ouX9GXvzC5UqGra79xFcHDTkzvfrkMZV5KNgCrPXv3SFfmWa/KTVb3yjW2qOmwmXE70901dKbJtCTTr9R/RyGhFy8k8vHtde+UF94jIzYxwEH14sQbP60HV9b+j1Q3d22YiMuycsDCZ+qtC6hW0be9lvWsV/WthmgEj2Ey/N2X6ksoEjEUj7fND913dFohf8AJGTy9I5hDxBtfrHSDLwxkX8tIfKPlIgfKHbzz71YL2Tgsiq271tqRm9GRFH80tTcAqbmfK75v2eBM4bkKtXBt85OIZWrlzPcL7CtE9FIJisV7vwHeLMSpOMelSg9FmxSs4H6LghJCa1Vu6rJc9GfQaVPdN+hDBJl0/HOjJDJQB4GRTdDTeF1w/bzah9LcRLXHnR7CaxbMp+s8EKMb05boXizbgs1c9e/nLhWsvAe1nTKoXx5wuIv8OTEWp83rStV5v1oJGh5wnFIvZNSVhNh/biZMd7+c/lwa/v0rR1f0U4GFU6Q3FNj4wc/5E1wQjRgsLSlV5CLVXXPncZRvy1x3Dtxy1njW13HSn49nWRFzKjMWYGCJRQGwo3eC0Ns1wmj/0rb7U6VHpEnHIZHbR0/s8IgEC6tISG88j00/rdU3AGLXD/R5L/0EC1ccVXX3lhst+X6jOMXm9Sj/qfrM5CSSBEeKUTtoqZjeL4y0v5Ahrbgie103/M/bEIZxJfNfDrnUFDQxHluJa67599fNf3zk63ft7ojwIbfHF80XkDkbxgESC9PQ+TyazE5GAp/Clafpq8WFNEXhX3p9V27n0LlV7dukSBNUsnpcASKnaM/Y3UXvjgL4/q7bz1hVW9WxKWMnoD0atzWxX7EnT2HjvfjAxJw74m9nqOpcuV9VFQLq4OxVV3exp4tOHsPldyTA88K+xw2+SKB/HP+ZWEKz1VgSdcbOn80be4X5/4V199eau+OJaB5YgcjbD78x4oH9W1VtqO2+950DbB++Ft0nuhfbKL0yyhk8JepIohwApFZ5BdVUy2f34xMzdRTbZscNgsv4fCI1BY5O1DJEAAAAASUVORK5CYII=)}.bk-sidebar{box-sizing:border-box}.bk-button-bar .bk-bs-dropdown{padding:10px 10px 0 5px}.bk-button-bar .bk-bs-dropdown a{color:transparent;font-size:0;display:block;float:left;width:13px;height:13px;margin:5px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGdSURBVCiRXZJPSxtRFMV/dyYTunBREASRIkKwddPSjYIL+x2K9plR+owR7K7L7vsZutIkZqhO8jZ+h25aQilUkbaUgv2zEOpGpAsZnbld+BJC7ubAvYd77uEeYaTaB25bhJqi44Kcq9LYWDOt/lxVkQG542ZFaQP3gB6wBLwDFoGfKtQ2qua7qhIAJB1XEeUE9CtSPLCxWQbObGxWEL0P+k2Uk3barQBImrrgWvmE8NnGpgaQpO4OcArM2Nhc+d4e8DCUcL6UwZYIk0WYzw9Zy4CnHm+9hNcvJI/+3GhuJUndR+A3yCpoCGQ2NkWfnKQuAMqo5Ih2gakScBeYBj32vE3gw5DqAtBCFGAcuCwBF8CRIFX1SiNf6AGPQXLwSgq7Aq+zMGPLrPdNB16h50+92uvul4MiWlJ4FZShAZxFebQztL0MHHoEICiiBvCrRJiI31wBvii8zQt9WV9f/Zek7sjG5lFzvzsWBvJGYA2Ye1599mOQiKTjZlVpCcwA74En/UQonIqwaX0ihJFKUlcH6sAE8Bdo2tg0B/9S5T8JNaZ11wlT0wAAAABJRU5ErkJggg==)}.bk-button-bar .bk-button-bar-list{margin:0;padding:0}.bk-button-bar-list>li{list-style-type:none;float:left;padding:0;margin:0;position:relative;display:block;overflow:visible;background-color:transparent}.bk-button-bar-list>li:last-child:after{content:"|";font-size:90%;color:lightgray;display:inline-block;float:left;height:28px;line-height:28px;padding:0 3px}.bk-button-bar-list.bk-bs-dropdown:after{content:"|";font-size:90%;color:lightgray;display:inline-block;float:left;height:28px;line-height:28px;padding:0 3px}.bk-button-bar-list[type='help'] li:after{content:"" !important;display:none}.bk-button-bar-list>a:after{content:"|";font-size:90%;color:lightgray;display:inline-block;float:left;height:28px;line-height:28px;padding:0 3px}.bk-button-bar .bk-button-bar-list .bk-bs-dropdown-menu{padding:10px 8px}.bk-button-bar .bk-button-bar-list .bk-bs-dropdown-menu li{float:none;clear:both;font-family:Helvetica,sans-serif;line-height:1.5em}.bk-button-bar .bk-button-bar-list .bk-bs-dropdown-menu li input{margin-right:8px}.bk-button-bar-list .bk-toolbar-button{width:30px;height:28px;padding:5px;border:0;border-radius:0 !important;-moz-border-radius:0 !important;-webkit-border-radius:0 !important;background:transparent !important}.bk-button-bar-list .bk-toolbar-button .bk-btn-icon{display:block;position:relative;height:16px;margin:0;border:0;background-size:contain;background-color:transparent;background-repeat:no-repeat;background-position:center center}.bk-button-bar-list .bk-toolbar-button span.tip{display:none}.bk-button-bar-list .bk-toolbar-button span.tip:before{display:none;content:" ";position:relative;width:100%;background-position:top left;background-repeat:no-repeat}.bk-button-bar-list li::hover .bk-toolbar-button{cursor:pointer;background:transparent !important}.bk-button-bar-list li:hover .bk-toolbar-button span.tip:before{display:inline-block}.bk-button-bar-list li:hover .bk-toolbar-button span.tip{z-index:100;font-size:100%;color:#fff;font-family:'Open Sans',sans-serif;white-space:nowrap;background-color:#818789;border-radius:3px !important;-moz-border-radius:3px !important;-webkit-border-radius:3px !important;display:inline-block;position:relative;top:25px;padding:3px 5px;transition:all .6s ease;-webkit-transition:all .6s ease;-moz-transition:all .6s ease;-o-transition:all .6s ease}.bk-button-bar-list li:hover .bk-toolbar-button span.tip>*{display:block;text-align:left}.bk-button-bar-list li:hover .bk-toolbar-button span.tip span{width:200px;white-space:normal}.bk-button-bar-list .bk-toolbar-button.active{background:#fff;-box-shadow:none !important;-webkit-box-shadow:none !important;-moz-box-shadow:none !important;outline:none !important;border-bottom:2px solid #26aae1}.bk-button-bar>.bk-toolbar-button.active{border-bottom:2px solid #26aae1}.bk-plot-above.bk-toolbar-active{border-bottom:2px solid #e5e5e5}.bk-plot-below.bk-toolbar-active{border-top:2px solid #e5e5e5;padding-bottom:45px}.bk-plot-above.bk-toolbar-active,.bk-plot-below.bk-toolbar-active{height:30px}.bk-plot-above.bk-toolbar-active .bk-logo,.bk-plot-below.bk-toolbar-active .bk-logo{float:left;top:5px;margin:5px 0}.bk-plot-above.bk-toolbar-active .bk-button-bar,.bk-plot-below.bk-toolbar-active .bk-button-bar{float:right;position:relative;top:5px}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-button-bar-list,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-button-bar-list{float:left}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown{margin-right:20px}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown:before,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown:before{right:-6px}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown:after,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown:after{right:-12px;position:absolute}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-button-bar-list .bk-bs-dropdown-menu:after,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-button-bar-list .bk-bs-dropdown-menu:after{content:""}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-toolbar-button,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-toolbar-button{float:left}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-toolbar-button.help,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-toolbar-button.help{float:right}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-toolbar-button.help span.tip,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-toolbar-button.help span.tip{right:0;text-align:left;width:200px;white-space:normal}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-toolbar-button.help span.tip>*,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-toolbar-button.help span.tip>*{margin-left:0;margin-right:0}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-toolbar-button span.tip,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-toolbar-button span.tip{top:41px;left:0;z-index:100;position:absolute;width:auto;padding:0 10px 5px 10px}.bk-plot-above.bk-toolbar-active .bk-button-bar .bk-toolbar-button span.tip:before,.bk-plot-below.bk-toolbar-active .bk-button-bar .bk-toolbar-button span.tip:before{top:-7px;left:-5px;width:100%;height:9px;padding:0 10px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAJCAYAAAAGuM1UAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3NzIwRUFGMDYyMjE2ODExOTdBNUNBNjVEQTY5OTRDRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDQjA4MDBGRDQ3NjExMUU0QjI1NEVEQTlCODRBRDIyNiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDQjA4MDBGQzQ3NjExMUU0QjI1NEVEQTlCODRBRDIyNiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6N0Y0M0E0Nzk5NDIyNjgxMTk3QTVDQTY1REE2OTk0Q0UiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzcyMEVBRjA2MjIxNjgxMTk3QTVDQTY1REE2OTk0Q0UiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4te1g5AAAAk0lEQVR42mL8//8/AymApamjC5dcJRBPBOJvyIJM2FQCbS0GUm1APAddDkPDv3//3BgZGTuh3Eig5lKcGv78+aPKxMS0HMhkhokBNbcDDfHApoGHmZl5HZAWQrOUGWQIyDBkDYxAqxcBTdPBEQACQMM2AGk+Jqgn64CKA/EFJ1BeC2QoE9B9AUBOPTFxAFTnDxBgAI5eL2ABBdyaAAAAAElFTkSuQmCC);display:block !important}.bk-plot-left.bk-toolbar-active{border-right:2px solid #e5e5e5}.bk-plot-right.bk-toolbar-active{border-left:2px solid #e5e5e5}.bk-plot-left.bk-toolbar-active,.bk-plot-right.bk-toolbar-active{display:block;margin:45px 0 0 0}.bk-plot-left.bk-toolbar-active .bk-logo,.bk-plot-right.bk-toolbar-active .bk-logo{left:6px;margin-bottom:20px}.bk-plot-left.bk-toolbar-active .bk-button-bar,.bk-plot-right.bk-toolbar-active .bk-button-bar{position:relative;left:3px}.bk-plot-left.bk-toolbar-active .bk-button-bar:before,.bk-plot-right.bk-toolbar-active .bk-button-bar:before,.bk-plot-left.bk-toolbar-active .bk-button-bar:after,.bk-plot-right.bk-toolbar-active .bk-button-bar:after{content:" ";display:block;height:0;clear:both}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list:after,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list:after{content:" ";height:0;display:block;clear:both}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown:before,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown:before{top:}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown:after,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list.bk-bs-dropdown:after{content:" \2014";float:none;clear:both;display:block;width:30px;height:8px;line-height:8px;padding:3px 0;text-align:center}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li{clear:both}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li:last-child:after,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li:last-child:after{content:" \2014";float:none;clear:both;display:block;width:30px;height:8px;line-height:8px;padding:3px 0;text-align:center}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li .bk-toolbar-button.active,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li .bk-toolbar-button.active{border-bottom:0;border-right:2px solid #26aae1}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li .bk-toolbar-button.help span.tip:before,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li .bk-toolbar-button.help span.tip:before{left:-57%}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li .bk-toolbar-button span.tip,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li .bk-toolbar-button span.tip{position:absolute;top:4px;left:40px;padding:5px 10px 5px 10px}.bk-plot-left.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li .bk-toolbar-button span.tip:before,.bk-plot-right.bk-toolbar-active .bk-button-bar .bk-button-bar-list>li .bk-toolbar-button span.tip:before{top:2px;left:-19px;width:9px;height:15px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAPCAMAAAABFhU/AAAAA3NCSVQICAjb4U/gAAAAY1BMVEX////////8/Pz5+fn39/f19fX09PTv8fHv7+/t7e7s7Ozp6enn6Onm5ubj4+Ph4eHf39/X2drW1tfMzMzAw8S+wMGusbKorK6orK2nq6ufo6WcoaGYnZ+RlpiJj5GGjI6Bh4n1ho2QAAAAIXRSTlMA//////////////////////////////////////////9G9E6kAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABR0RVh0Q3JlYXRpb24gVGltZQA5LzUvMTTY+fXxAAAAUklEQVQImTXN2xZAIABE0VQUIfdLwvz/V1rL1DztpzOi4EoIQoekNoIaH1AL8EvvoExEUkBWfWZZvyWVzq/vL6kbP9/sKdtPF8vKdMPBN1m5AR+0BAnD6uP50QAAAABJRU5ErkJggg==)}.bk-bs-caret{color:lightgray;display:inline-block;width:0;height:0;position:relative;left:11px;top:3px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.bk-hbox{display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:-ms-flexbox;display:box;box-orient:horizontal;box-align:stretch;display:flex;display:-webkit-flex;flex-direction:row;flex-wrap:nowrap}.bk-vbox{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;width:auto}.bk-hbox-spacer{margin-right:40px}.bk-vbox-spacer{margin-bottom:auto}.bk-button-bar{margin-top:0;margin-bottom:0;padding-top:0;padding-bottom:2px;position:relative;display:inline-block;vertical-align:middle}.bk-button-bar>.bk-bs-btn{position:relative;float:left}.bk-button-bar>.bk-bs-btn:hover,.bk-button-bar>.bk-bs-btn:focus,.bk-button-bar>.bk-bs-btn:active,.bk-button-bar>.bk-bs-btn.bk-bs-active{z-index:2}.bk-button-bar>.bk-bs-btn:focus{outline:0}.bk-button-bar .bk-bs-btn+.bk-bs-btn,.bk-button-bar .bk-bs-btn+.bk-bs-btn-group,.bk-button-bar .bk-bs-btn-group+.bk-bs-btn,.bk-button-bar .bk-bs-btn-group+.bk-bs-btn-group{margin-left:-1px}.bk-toolbar-button{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px;color:#333;background-color:#fff;border-color:#ccc}.bk-toolbar-button:focus,.bk-toolbar-button:active:focus,.bk-toolbar-button.bk-bs-active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.bk-toolbar-button:hover,.bk-toolbar-button:focus{color:#333;text-decoration:none}.bk-toolbar-button:active,.bk-toolbar-button.bk-bs-active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.bk-toolbar-button.bk-bs-disabled,.bk-toolbar-button[disabled],fieldset[disabled] .bk-toolbar-button{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.bk-toolbar-button:hover,.bk-toolbar-button:focus,.bk-toolbar-button:active,.bk-toolbar-button.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-toolbar-button{color:#333;background-color:#ebebeb;border-color:#adadad}.bk-toolbar-button:active,.bk-toolbar-button.bk-bs-active,.bk-bs-open .bk-bs-dropdown-toggle.bk-toolbar-button{background-image:none}.bk-toolbar-button.bk-bs-disabled,.bk-toolbar-button[disabled],fieldset[disabled] .bk-toolbar-button,.bk-toolbar-button.bk-bs-disabled:hover,.bk-toolbar-button[disabled]:hover,fieldset[disabled] .bk-toolbar-button:hover,.bk-toolbar-button.bk-bs-disabled:focus,.bk-toolbar-button[disabled]:focus,fieldset[disabled] .bk-toolbar-button:focus,.bk-toolbar-button.bk-bs-disabled:active,.bk-toolbar-button[disabled]:active,fieldset[disabled] .bk-toolbar-button:active,.bk-toolbar-button.bk-bs-disabled.bk-bs-active,.bk-toolbar-button[disabled].bk-bs-active,fieldset[disabled] .bk-toolbar-button.bk-bs-active{background-color:#fff;border-color:#ccc}.bk-toolbar-button .bk-bs-badge{color:#fff;background-color:#333}.bk-canvas-wrapper{position:relative;font-size:12pt;float:left}.bk-canvas{clear:both;position:absolute;font-size:12pt}.bk-canvas-wrapper .bk-canvas-map{position:absolute !important;z-index:-5}.bk-tooltip{position:absolute;padding:5px;border:1px solid #1e4b6c;background-color:#1e4b6c;border-radius:5px;pointer-events:none}.bk-tooltip.bk-left::before{position:absolute;margin:-7px 0 0 0;top:50%;width:0;height:0;border-style:solid;border-width:7px 0 7px 0;border-color:transparent;content:" ";display:block;left:-10px;border-right-width:10px;border-right-color:#1e4b6c}.bk-tooltip.bk-right::after{position:absolute;margin:-7px 0 0 0;top:50%;width:0;height:0;border-style:solid;border-width:7px 0 7px 0;border-color:transparent;content:" ";display:block;right:-10px;border-left-width:10px;border-left-color:#1e4b6c}.bk-tooltip.bk-tooltip-custom.bk-left::before{border-right-color:black}.bk-tooltip.bk-tooltip-custom.bk-right::after{border-left-color:black}.bk-tooltip.bk-tooltip-custom{border-color:black;background-color:white}.bk-tooltip-row-label{color:#9ab9b1;font-family:Helvetica,sans-serif;text-align:right}.bk-tooltip-row-value{color:#e2ddbd;font-family:Helvetica,sans-serif}.bk-tooltip-color-block{width:12px;height:12px;margin-left:5px;margin-right:5px;outline:#ddd solid 1px;display:inline-block}.bk-canvas-map{position:absolute;border:0;z-index:-5}.shading{position:absolute;display:block;border:1px dashed green;z-index:100}.gridplot_container{position:relative}.gridplot_container .gp_plotwrapper{position:absolute}.table_wrap table{display:block;margin:5px;height:300px;overflow-y:scroll}.bk-table{overflow:auto}.bokehdelete{float:right}.plottitle{padding-left:50px;padding-bottom:10px}.bk-toolbar-button.hover:focus{outline:0}.bk-tool-icon-box-select{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAgCAYAAAB6kdqOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBODVDNDBCRjIwQjMxMUU0ODREQUYzNzM5QTM2MjBCRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBODVDNDBDMDIwQjMxMUU0ODREQUYzNzM5QTM2MjBCRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkE4NUM0MEJEMjBCMzExRTQ4NERBRjM3MzlBMzYyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkE4NUM0MEJFMjBCMzExRTQ4NERBRjM3MzlBMzYyMEJFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+hdQ7dQAAAJdJREFUeNpiXLhs5X8GBPgIxAJQNjZxfiD+wIAKGCkUZ0SWZGIYZIAF3YVoPkEHH6kojhUMyhD6jydEaAlgaWnwh9BAgf9DKpfxDxYHjeay0Vw2bHMZw2guG81lwyXKRnMZWlt98JdDTFAX/x9NQwPkIH6kGMAVEyjyo7lstC4jouc69Moh9L42rlyBTZyYXDS00xBAgAEAqsguPe03+cYAAAAASUVORK5CYII=")}.bk-tool-icon-box-zoom{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAgCAYAAAB3j6rJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMjFERDhEMjIwQjIxMUU0ODREQUYzNzM5QTM2MjBCRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMjFERDhEMzIwQjIxMUU0ODREQUYzNzM5QTM2MjBCRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMyMUREOEQwMjBCMjExRTQ4NERBRjM3MzlBMzYyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMyMUREOEQxMjBCMjExRTQ4NERBRjM3MzlBMzYyMEJFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+a2Q0KAAAAmVJREFUeNq8V19EpFEUvzOtmKfpJSJKDL2WiLJExKaUEq0eeikiaolZLT2lVUpPydqHqIlIo1ilFOmphxj1miKWWHppnobIt7+zeyZ3jjvz/bnf9OPHd8/9d77z3XN+94ts7ew6SqksWKX+w1GFiLjYdVSAfeAQ2Ag2sf0GvAXT4C/wle1x3lt9UOGBNk6BrYa+FuYIeAWOsmNviGqe6W+q081OmAGvizgh0cpjZ3RjGBFZBpMG+xn4wM8NYJfWFwNXwXrwS96RiIUTwwYn6AxMgb+FvQ5c4zOUxzR4Ce5GLZyo5LfSsQP2G5xQbKO+bWFfoLWinA1OAEcoM2rFRpMe5sloJWgtm4j0iPZcPhVdkOWxBWvZONIi2uc+5sqxbTaO1Ij2o4+5T6JdGy1SF4Kg2mLsi01E/oh2l4+5HTKaNlmTEe0ka40XyNqTsYnIkWiTwC16rMRNci0bR0hJ7w1veizqy9uB5D4ZDZKBtI3WvLCCJoT9E3jHny4j1DdmWOcbrWWjNYuGoqaL2kdmKayTztio7yzTJprz4A/9PuI3a8YMh5IKVC9fetxAY5rB79pNzXdESMJ/GrSjm8/DCTjAgpjQZCDDh5I+w4HuQBBHOsE9USty4KB2KF85m9J+v5XX9KXr3T7fQZS26WefYlcU+ayJlxhDIT40jBnn21hQOPrfgFtEqAhdGETqK7gZ4h/Av4g4Jf5TUoYquQSuqJDhFpEJca3b4EoYOtyyhrSkHTzlcj4R4t4FZ9NL+j6yMzlT/ocZES9aky3D3r6y5t2gaw3xWXgs7XFhdyzsgSpr2fFXgAEAmp2J9DuX/WgAAAAASUVORK5CYII=")}.bk-tool-icon-help{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBODVDNDBDMzIwQjMxMUU0ODREQUYzNzM5QTM2MjBCRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBODVDNDBDNDIwQjMxMUU0ODREQUYzNzM5QTM2MjBCRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkE4NUM0MEMxMjBCMzExRTQ4NERBRjM3MzlBMzYyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkE4NUM0MEMyMjBCMzExRTQ4NERBRjM3MzlBMzYyMEJFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+mR+SmAAAA/BJREFUeNq8lulPU1kUwOnjCS2yL12pFZFRoBU1MYpCRVGD0cREo4kziX/ffDDxkzNmcEUUd0cBGYdhKYXSln0riyz+mNvceb6W15dxMvdDc3rfvb9z7rnn3HMsW1tbWTsPvkaisfFoND4xiTw9M8tkaUmxxWKxV5S7nE6304FsQLDspGB5ZeVDd89wKLz25YvB/pxdu/b6vA0Bv81qNatgY2PjY++nvs/96+vr/C0pLvJWetxOZ3Z2NrYzwzlYE4lGw6NjM7NzzKiqWnug5lB9HWsyKMDwRx2dk1PTyD5v5ZFDgcLCAoMTzM8vvP/YHQqPIpeXlZ4JNumO8o0CTIO+lEjk5+9ubjxRUV6WZW5MTE51POtiY57NdvZ0szioXgG2373XnlhedtgrWppP5ebkpLoukVguKMhPq2N1be3x02ex+AQ6LrWdl+dIKtjc3Lx3/yGecTrs51qCiqLInajs7ftjYHBY3nZRUeE+n++HmmqdEUDaHz1BB75qO3dWQJIK8GN3bx/WXTzfas3N1TrtweMODpdqMhcbPNVY6XZpJ1dWV3/57f7i4lKgvpb7Y0YRNhIzCE0njmvp6H7a9UJL1zqXGMPv7NUqYDsQBIDi07aCnk99rCZmdLdKis3NzQsZS3+6ce1y24Ufr1/1uJxSx+BQSHcyEhAUn8BuK0DqHxhCEifSDhJYyseOHhExjmeOHm6Q8zOzs6neEyiwwJXwWITwIHJS431pKSEEAkMbPDbbP5Ge9p0ABRAscDUWjzPlcblS151uOpk2IkMjo1IuKS5OuwYg4QRcEbluPqeGR8Kv372XD9H+6qq0ywQQuLqwsChC2wz9c//Ayzdvk46yWltbgqn5KHOFX+CqSB9syUgfCo28evtOerk12LxTVksgcDXL9CCfRVZysSSqNmMMhiJVZVzKQySdk5EuHaOIY/LqZlTAMyDzK+NiAQSuUE+QqIhZ/+kQQOCqw27/869BypO/7qDxnls3b5hXEPn7FQCueD1u3gCSwoyXTA5Q0VgcLHCeFnX/vr0E+O/dPTy/Bpf28+07Qt6dl3ftymUDBaD4ramuAr79mgbq65BI0anpme83n6oFCqC/rjb5XPOW0RMgdDzvovJ9D51Io4QgAASbVMCg4yC5yewnnc+pfP+OzkZKEBBQAPVFnw+/tj/AhJ2KvvGQRZ8cpO7KV+SbtoU7oG2h1PE5eLKxrLTEfNvS+eIlJuIWWiPtRqPGq8q353DAb/CiiXPTMHCrphov2f986OmlaiPwF3O8HsqwQ9c6jkdj4bExEXh84lYb/PWZW8f/o/nVBQa9RWR8HC/r2ndqltvlon3Xdmmp46sAAwDlJz2CuiavpwAAAABJRU5ErkJggg==")}.bk-tool-icon-inspector{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADEUlEQVRYR81XXVIaQRCeHqug8CXmBNETaE4gniDwIgpVspxAbxC9ATkBkCpQ8gKeQDiB5AQxNyAvUlrldr7eHxyGXZi1rMJ5opbp7m++7un+htSGF204vsoMoNXrlzSpfWa1oxQfhAegCZGaEtPorHo8znIoJwCt6+td8uk7ApUQCIHTF4BNAWzImq8ap6cP68CsBdDp9i9ZqXM7ML79g/EnCWD+jgMKENKqWT+tXK0CkQqgNRjs0OxpQIqKhoMxaG6/6JeRnK7T6yO2UvVqhYSlLX+ryORfgKn9ORDFIy7ky41yGcwsr0QAQfDH5zucOswx819fs4egI9OFCcD8DjBF7VNbEX0JzdWEt3NHSSASAcCxBDqMgt/623kvyTgNgNjJIfTjk4D4FqaJR1715MjmYAmA5Bx3AwUXQL+t105KaTlcBSC26XRvhjEIoLiq1yqXpr8FAGG16/ug4IT27fxBWu7EiQuAiImJpEMKE6nYM30uAIDDttSUOPfJP7JzbjPhAiBIh9QE67vIvoOi9WJfCwDavf40ulpjbCqmUf+W753ezURuh7Dg1SqflwAEHU6pgfyBq9Y4qx0LG++2fnZ/eUzcstmdM2AWH+jfc+liWdBJfSENf8Lifi3GVwC9mybOfi5dzatWVrbbLIHNva8p5h/16gkaFiLGGxbufkoE6XguwePiXLF3XmMfCUCUAqtKXU7sumd1CowOuJEi3Pg1FBpjitIGhyvVSfvmjci6ZR+rFQfDiPVE2jFYeICQ+PoewwjC5h7CZld6DBdyu6nDSKgzOyIMhmhK5TTqXYbRorZYM46TmpKAAOrGWwSJJekSB1yqJNOzp1Gs7YJ0EDeySDIMtJbQHh6Kf/uFfNFZkolJICRmz0P8DKWZuIG2g1hpok+Mk0Qphs0h9lzMtWRoNvYLuVImUWrmPJDlBKeRBDfATGOpHkhw670QSHWGLLckmF1PTsMlYqMJpyUbiO0weiMMceqLVTcotnMCYAYJJbcuQrVgZFP0NOOJYpr62pf3AmrHfWUG4O7abefGAfwH7EXSMJafOlYAAAAASUVORK5CYII=")}.bk-tool-icon-lasso-select{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAgCAYAAAB6kdqOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3NzIwRUFGMDYyMjE2ODExOTdBNUNBNjVEQTY5OTRDRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1ODBEQzAzNDQ0RTMxMUU0QTE0ODk2NTE1M0M0MkZENCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1ODBEQzAzMzQ0RTMxMUU0QTE0ODk2NTE1M0M0MkZENCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTU0QzIwMUM1RjIxNjgxMUE3QkFFMzhGRjc2NTI3MjgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzcyMEVBRjA2MjIxNjgxMTk3QTVDQTY1REE2OTk0Q0UiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7r0xDwAAAC9klEQVR42sSXb2hNcRjHz50rt1aslNQitSimq6VESW6SFMvFyJ+UknnhhVhkRIkX/iRbSPMnyt95sblZFvMC02patEKtaE3Km1taqWlxfZ/6Hj39+p17zr3nHJ76dO4953d+53ue5/k9v+ck2jseORHYRDAXpHmcDSar84McNwLegwHQa5soGULENFAPMmApH+5laXVcw9/fwA1wDYyFEbQI7FITl2vTQTPYDnaCj3KyooQJVoNu0BmBGG0zQc71YhAPzQEnGRY/+8R8+QGGVCjcXEqBZQy3tkrQBpYnfRL1EGgEEzzGSB48AT2gT+eCj8nLbQCbDU9lk0USto35Ytov0MWE7C8zTL3kKbiiFsQqWw7VcaBNzD2wGOwJIUabePeB+l9tCloI2i0xlnCsBAfAVyda69Pe1yGbBW4ywVwbB2fBRSc+0y8/5AqSpL0KpqqLo2BHRKHxMnnuFvW/xxUkD65VF76DBpb5OG0vy8rfFVtBrzQbA/f9AzFZ0KT+t0iKiKCNRt7kuMriNAlTq6pvkti33Eq9whh8N0YhUqlPcP9ybRjs1pvrfEv5j8NkyzgFatS5PNjKo+NurinjxtqIhcgedh3cN8SIZ9by6GhBI8YEkuBVHpNXlyAkQyHP2SloG7CJcQW9tOzu3VwFlVyFl8Bn8AZ8AMctnk1RxFHwDtyxCBG7DNbrMGlLoIWVXfaVR8f3ExQsDxf7wpeZwp067eMxaUsOg7fFBiUZsiPgjOX6pCL3zgDbAvZIp8HjIHF2K/VturDVqElhrJ8tShdbFqcUQW4rIK3FfrCpTGHS47wGHZbFEsjM9iPP8M3j/pYPOI+smgV8kZZyxRRr8sfZlh4LOI/0UReiiLPfV4e4/pwlB3571J3GsIKCfHWcp7cyLIzyNfGCHqkzxjaxzR0tV1CiUChYLzzszPndKx3mM0vyH+SqdRrW1UfnIT2Zh7hhtilZ4/wSV1AcOeRntmJXE2dS+9mg5VzV/xRkq1NjYSb8I8AAdTOa+zQjMmsAAAAASUVORK5CYII=")}.bk-tool-icon-pan{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCRTI5MDhEODIwQjUxMUU0ODREQUYzNzM5QTM2MjBCRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCRTI5MDhEOTIwQjUxMUU0ODREQUYzNzM5QTM2MjBCRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJFMjkwOEQ2MjBCNTExRTQ4NERBRjM3MzlBMzYyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJFMjkwOEQ3MjBCNTExRTQ4NERBRjM3MzlBMzYyMEJFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+OXzPwwAAAKNJREFUeNrsVsEKgCAM3cyj0f8fuwT9XdEHrLyVIOKYY4kPPDim0+fenF+3HZi4nhFec+Rs4oCPAALwjDVUsKMWA6DNAFX6YXcMYIERdRWIYBzAZbKYGsSKex6mVUAK8Za0TphgoFTbpSvlx3/I0EQOILO2i/ibegLk/mgVONM4JvuBVizgkGH3XTGrR/xlV0ycbO8qCeMN54wdtVQwSTFwCzAATqEZUn8W8W4AAAAASUVORK5CYII=")}.bk-tool-icon-polygon-select{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAgCAYAAAB6kdqOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFMzNBREIxOTQ0MUExMUU0QTE0ODk2NTE1M0M0MkZENCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFMzNBREIxQTQ0MUExMUU0QTE0ODk2NTE1M0M0MkZENCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkUzM0FEQjE3NDQxQTExRTRBMTQ4OTY1MTUzQzQyRkQ0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkUzM0FEQjE4NDQxQTExRTRBMTQ4OTY1MTUzQzQyRkQ0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+xB9jgwAAAe5JREFUeNrsmL1LAzEYxu9KUVDBW8RBhRscXNSCoyA6uIl0kYqIXFcXBRdBoYpuDi7iYEFbkFZPpX6sin+BtAhODloHRZTaSkEUUZ/A23rUer275mjFBn40hJA8eZI3ea+iGjn4FL5LCkigHiQ5trM5HEPuQaFQcQhlVpy0GoFWpF2hmKe/lfaUWUHZYsRSM2Vn/9CSQ5LNu2Bq/LI7Qw6KgqSNc5gavywdqgiqRFklyv7doS7q7flrUbYImkG61FvmAU9gBvhLHWUrYIucfwdxM6kNL4fqwBzV18AHOAaNYJo1BsOqDFyiKAp68BA0Cx6BD4yDc8ql+0FC008Gp4HQtttOh6JgAVSDF/BM7WmdZyQCUct6giSTkdYCpqjup+0JghqwaXCMSYhibknFOFQFwnRIl0AbWKXtUSy42wuuIMplNcoewDB9XdyB2gLbYzQTiEKUYtShHjBK9RM6JxOgCZxxvCo2IIohOX/pwMJ1D3STCBWMgTeCZyYQI+I/3jKNmFuNe5d0zyRsSt68yojnOl+UeUEXuAc3dLew67WTs5gYzZUpvtxD3UEurINdam8HDeCIsyNMTB8cCeA344qCsyNrBbFOrfQPxQWHyCkkJhPR8/lcYoJe6XJj98GAXXkIE6IRI+S4lHXoS4ABAP0ljy6tE4wBAAAAAElFTkSuQmCC")}.bk-tool-icon-redo{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAgCAYAAABgrToAAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wwGEDEBYlsi0wAAAYBJREFUWMPtl71Lw0AYxn9ppVAodKoUBGfHDtJJR0FRFAc5uMEbBFcdBcXi4G5Hhw5ZAkFQHASho07i0L+hUCi4KBSKQsHlLYSS0iQ0rcI9EMjHfTz3e58LCVhZWf1vOVEbup6fBTbkWAOyQEUet4AB8Ao0gabRajATg67nl4ErQAHFiON+AT5QM1p1UzHoen4eOAdOgELC8XtAHbg2WvWnZlCoPQLVKUXpDdhLQtMJMVcRc8sh7TvAA/AEfEj2kCyWgG1gH1ga03fHaNVKbFDIvYdM0AVqQGNS+GUzHUluyyEmV+OQdAID54CXkLI+AwdGq16clbueXwDugM2Qcq8brX6ijLMQOL8MMVc3Wp0mCZ0saMv1/BvZaENVZa6Lqb4Hk0pKfg/sjuzuFaNVZ1L/TNoGJbOHkr+hCsDZnyAYIHkM3AZu9YHFSdnOMDs1gHbgOj9S9tkTdD2/CHzGjIQzL4Lpfs2kTXKUnCU4hmQO+I5Cbl4ES/YfwcrKyiqefgEvB2gLTkQWKgAAAABJRU5ErkJggg==")}.bk-tool-icon-reset{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAgCAYAAABgrToAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCRTI5MDhFMDIwQjUxMUU0ODREQUYzNzM5QTM2MjBCRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyOUMzNDE3NDIwQkIxMUU0ODREQUYzNzM5QTM2MjBCRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJFMjkwOERFMjBCNTExRTQ4NERBRjM3MzlBMzYyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJFMjkwOERGMjBCNTExRTQ4NERBRjM3MzlBMzYyMEJFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+kFHGtQAAAm1JREFUeNrMmE9ExFEQx3+7ZYmlLrEsUUTHaEV0iESJVqduXaJr1xKlFB1bdYqoQ9GlFBFdikgpIhLd0rLqUsQqrW2G7+YZr+2993vaHT6H3583M795897M+0U2t3cCR6kh+kA3rtvx7IYoEGfEMSi4GIk4OJgg5ogRot5wzBvBhmaJnI2xqMW7dcQC8UCMWzgX4N1xjF2ALq8OctROiGkiHrhLHDpOoNOLg5xXF0Sn5lmWWCUGiBRRC1K4t4p3pLCuKyVnnXMwAUVJcT+HfFo3SH5ePGPI24TmA1Pl8rJcBGPEvsa5I6KVWDNcmQW824qxqiRhI+bi4IxmWjOYuneH/HvH2Ixmumd8bjNhhad8lxgSzrfp8jUa/L/wlI8KZ3h1T4bdB30Kb9zz4t6YbgurlIMBdoBHUQiGTBx8JYoKPqVe0ftFNInnW8J20SSCjRWM8k8E1S+TNfbZYyQ59yJEg0kjw1QyB42k1iI6ReXLfEWSK8iHJnJVsYqN8jtammuFc/FOr3juU7Ia+39uM7fiuq8aVrEqp+J6BPWzahw8IPLKdTPKUNU4yJ3Fhqb1inu0y7qeRNVYsWkWFkXPl0QZ8iVbohFmW0s2DmY1jSUX8mUPzi1rmoLML2eXsvsgR/FO3JtAix53nNZ96FDlDrasW35eKGniRRPJeywck9VdOjTdayL3Ahv5MC1/xy+Hp1Iq7BGHMHatjOEqMUgMlxmbVsaEOpMk4GSnp0VyCedyLtuMTlhRD1ZaPoRjeejoMf1HE7VUPkW04Jz7Ztm9rGHslM1Hhjl2xlCn+4muQP/77RyHdf799uli5FuAAQC+l5Sj5nEBdwAAAABJRU5ErkJggg==")}.bk-tool-icon-resize{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAgCAYAAAB3j6rJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBODVDNDBCQjIwQjMxMUU0ODREQUYzNzM5QTM2MjBCRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBODVDNDBCQzIwQjMxMUU0ODREQUYzNzM5QTM2MjBCRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMyMUREOEQ4MjBCMjExRTQ4NERBRjM3MzlBMzYyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkE4NUM0MEJBMjBCMzExRTQ4NERBRjM3MzlBMzYyMEJFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+nIbQ0AAAAIJJREFUeNpiXLhs5X8G7ICRgTYAq31MDIMEwBzyERoCyJhWAN2ej4MqRFiIjUMahczgSyMsNE4PxACBQZlrcAFsuYkcLECpQwZNiIw6ZNQhow4ZdcioQ0YdMuoQerRZkQE/vdqwgypqQD7+MIBuANn9f1CnEcbRXIMjd4zM0QCAAAMAbdAPQaze1JcAAAAASUVORK5CYII=")}.bk-tool-icon-save{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMjFERDhENjIwQjIxMUU0ODREQUYzNzM5QTM2MjBCRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMjFERDhENzIwQjIxMUU0ODREQUYzNzM5QTM2MjBCRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMyMUREOEQ0MjBCMjExRTQ4NERBRjM3MzlBMzYyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMyMUREOEQ1MjBCMjExRTQ4NERBRjM3MzlBMzYyMEJFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+h5hT8AAAAKBJREFUeNpiWbhs5QcGBgZ+hgECTAwDDGAO+AjEjGj4Lw5xUrAAkl3ocr8IhQAzjT3PRu0o+I+EHw65NDDqgJHrABYC8t9JMIuRmiHACS2IKC0LOKH0X1JDAOTzs0BsBs3XlIKz5KSBRCA+RQXLjwNxNDlp4BoQm9Mo7fGPZsNRB4w6YNQBI94BfwfaAV9G08CoA9DbA/xUavkMvRAACDAAaPgYViexODkAAAAASUVORK5CYII=")}.bk-tool-icon-tap-select{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3NzIwRUFGMDYyMjE2ODExOTdBNUNBNjVEQTY5OTRDRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCOTJBQzE0RDQ0RDUxMUU0QTE0ODk2NTE1M0M0MkZENCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCOTJBQzE0QzQ0RDUxMUU0QTE0ODk2NTE1M0M0MkZENCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTQ0QzIwMUM1RjIxNjgxMUE3QkFFMzhGRjc2NTI3MjgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzcyMEVBRjA2MjIxNjgxMTk3QTVDQTY1REE2OTk0Q0UiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6eYZ88AAADLklEQVR42rSXf2TUYRzHv7tuGcfE6Vwb5zLSSjEj7Y9KWqfEmFZJP+yPMdKKmUrrn0iUfjhWlLFi6YfNrF+StBoTo39iYkTGco4xxxG59P7k/T2PT8/37nu3bx9ezvPj+zyf5/PreS78bGLS8SmrwE6yje3NHJsDBTALpknBz6JhH3NiYAB0gHqPOVv52wJ6QQ48BzdAttTioRJjdeA8mAHHS2xuk3p+M8M16ipVQE49Ds6CiFO9RLjGONf05QLx6wPQaBlbBlPgJVgkP0ETiIJ2sB/E1XfimjfgBOOlKDUqCGOcqBcQnw6BYW5YTo4wbvQhMmCfGRemC2rBiGXzWUb+kM/NRZ6CHWBM9ce5R61NgX6ayhSJ5EPlItlDRNkz4JbFHf06BkSzHjXxM+gDv1S/mPUo2AXWgt9UUHL/IVhS8yUV1/EbV3o4N+NaoE9Fu/i827K5pNYHnqAVJECShWmAaddpscYFFXwR7vnXBRGlnUN/L6kqKJlxnRUuDbaDBiL+vst5d4gpcpBrqk/2jIgCKVUolhntplzivHmwh4stGOPfwBWwl/2dpp8p7xjQZqFLiQJtauKkivYm+kzccpK57yXfOUe+P23JqAnVbhMFmlXntCWnxbT31am9ZJ4BJifsUmNTqt0cYhA5ypympPg7VkEKunPbVb8cIG+0kyHLJZNR7fUMooUKFHAPkfQo58VLK+RzwRDd4FdWG9mjpaAXzqkJa1R7kQttqEABWXMjOOxxVRfnhRm5URX1prk/0pQHwNcKlchZ+jdpC+hFdVqO0my9Hj5dkYgCn1Rfh/KdlNDHrJhPqlDih+IfBd6qwpOgEqYMsorJ2HtWxtagLJDn/W3KRfPOZhoeBJfZPgVeGKeKrkQBh5dLXl25Ny3pc4/1fkTdbvFqFQgbxWeYD0hXulhQ0pYiM1jG547fcbMQpVnHTZEn9W3ljsCzwHxCdVteNHIZvQa7/7cC7nV6zHIfyFP9EXjFa7YxKAVqPP4bxhhoLWW+z9JyCb6M/MREg59/RlmmXbmneIybB+YC/ay+yrffqEddDzwGvKxxDmzhc0tc80XVgblqFfgjwAAPubcGjAOl1wAAAABJRU5ErkJggg==")}.bk-tool-icon-undo{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAgCAYAAABgrToAAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wwGEAgO/GCy+AAAAXlJREFUWMPtlr1LQzEUxX+1ohQKuhQK/Sc6SCcdBUVQFCSQwQwOjjoKisXB3a5Ch7c8CA6iKAgddRKHjs6FQtGpUBCEoksK5RE179FPyIEs+bg59+TcJODh4THdSA0qUBDqNLBq2jKQBopmuA50gWegBtSUFN2REAxCnQfOAQEsOC5rAxooKylaQyEYhDoDnACHQDZhmA5QAS6UFJ8DI2hUuwVKA3LIC7BlUzOVgFwRuAcKluEmcAM8AB/Gexgv5oANYPuXtQ1Dsp6YoFHu1bJBCygD1f/Mb4pp3/g2b0lwqV/JVAxyc8CT5VgfgV0lRSdmslngGlizHPeKkuILYDZGzDMLuYqS4iiJ6UxC60GoL02h9VAye506KxiEugC8Rar1Dthxvc+SYsZx3nGEXBPYGzY5JwWNV96BTF/3gZLiahRPnYuCmxFyDaA6trc4CPV3zBiLSor2uD04eb8ZByWHqtz0K/iHkvO9W35SqjiKnP/ne3h4eIwOP9GxagtPmsh6AAAAAElFTkSuQmCC")}.bk-tool-icon-wheel-zoom{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAgCAYAAABpRpp6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCRTI5MDhEQzIwQjUxMUU0ODREQUYzNzM5QTM2MjBCRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCRTI5MDhERDIwQjUxMUU0ODREQUYzNzM5QTM2MjBCRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJFMjkwOERBMjBCNTExRTQ4NERBRjM3MzlBMzYyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJFMjkwOERCMjBCNTExRTQ4NERBRjM3MzlBMzYyMEJFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+sFLapAAAA8xJREFUeNq8WH9k1VEU/+67ecTYxKM8xlJiifKIMUqUKMvy1CqbEmUxJZbSlGXTLBuJpYi18dpqStOzacT+WcTXpkiRUjziETEeY9bnzHm5O53vj/te7fDx3r3fc+/9fM/3nHPPvWWP0mOOIlVAC3AQqOc2SRZ4A9Cg58CSNrj1+FEnSIYfPynHTyOQArYCO/jRPPAJGAcmMM9f87vKfG3AF+AucMAgS5LgRZ4CH/mFrARkieAs8Aw4ASSBckaS++jZLOv6El4HjAKDwPoIa28GXgLdFmQv4WcO2BVBnXTmeIxK+D5wzLGXa8D1CGT78NPPhjFlGnjAmBbPSLefx65IBf+eZZ81hfznIfsr+W0eaACa2G3MhbuAt8CUD1kyRIfongDa4affhW4Nu2Oj0d2Bfg+6Y2UIukr2x4ShkAMOMQlNyLcmgVqj7z2wk17UDDosFOOYMOdPQ+dkyBcZFkb8DGxz2ckTwrKHA8g6HMn7gQWjbzsHqZSUmJ8sej6Cq7WzrhkzKVeYnmSEXSBM6I17RZ+WNWRfJ6z7K2xy1umUc7lGDizIkDL+AsNRXs6U3YpOUrRfWwS01K2noIuLzg+iTcFSiFLKlQPi8+aNAIwri24QlstaEM6JdoIsHBOdiyJl9RntfiXazUljEdJb3IKw1F10Q/Krtin0KaSD5Ido77MYK10sG0S4ByjzwW2LRT3pYlxLRBFpGM91/r9kRJuC/FbEnVEmhEwQYRqw7IMuC8LjnAKllSeBhEI0Qc8U636luWinWxYPqoFCnuxmX16VR9ldCvINqOH/NK5alpe8NY8qL5Nnl/GMFJhU6g2SZtqaw1xCkrss2pGEFhLp0CxuGow83+BDdoDn+FP8hJFeYusNlODL9LI/ubKLRRxDKfamuaNWRBx4o9TI49NDD9yjSdn9NKFa5jTGrdrIKpw1FJCtU8h6Rp/HwbVyBNOOSGtKGHJKtGdAao/NBO4aWrecS9mwQiuU8KLoi1nOEfepQ6TsFXVxnnO0NWFZEdVZjK8RaSgXoHtGbihwh4ViCM+LvhaL8VJ3xscdqnwOCk4xhDNKYNRHPOZfCakbzGOS+SWyloX8KsIj4lNScLwIuTsgsq+ASnFkmor4JdJayopKeEHZGOJ8OzMoatIkF0XvxIm5cGhcUtyhVqlrh4rNNoU8fI+jOCUs3cYIk14L63py9yo2D7fyBZ+t3AGuWgTmiFOCuCIvHuHFo6QbCpxm4GLIxZ+880j/K8Lm593EVZqnXF9N8UXIFt7zgwoeunDZCJzju44M+nKlEP4twAAD1RclkNDukAAAAABJRU5ErkJggg==")} | |||
|
2 | /*# sourceMappingURL=bokeh.min.css.map */ |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
@@ -1,240 +1,251 | |||||
1 | import ast |
|
1 | import ast | |
2 | import json |
|
2 | import json | |
3 | import requests |
|
3 | import requests | |
4 | import base64 |
|
4 | import base64 | |
5 | import struct |
|
5 | import struct | |
6 | from struct import pack |
|
6 | from struct import pack | |
7 | import time |
|
7 | import time | |
8 | from django.contrib import messages |
|
8 | from django.contrib import messages | |
9 | from django.db import models |
|
9 | from django.db import models | |
10 | from django.urls import reverse |
|
10 | from django.urls import reverse | |
11 | from django.core.validators import MinValueValidator, MaxValueValidator |
|
11 | from django.core.validators import MinValueValidator, MaxValueValidator | |
12 |
|
12 | |||
13 | from apps.main.models import Configuration |
|
13 | from apps.main.models import Configuration | |
14 |
|
14 | |||
|
15 | SELECTOR_VALUE = ( | |||
|
16 | (0, 'disable'), | |||
|
17 | (1, 'enable') | |||
|
18 | ) | |||
|
19 | ||||
15 | class GeneratorConfiguration(Configuration): |
|
20 | class GeneratorConfiguration(Configuration): | |
16 |
|
21 | |||
17 |
periode = models. |
|
22 | periode = models.IntegerField( | |
18 | verbose_name='Periode', |
|
23 | verbose_name='Periode', | |
19 | blank=False, |
|
24 | blank=False, | |
20 | null=False |
|
25 | null=False | |
21 | ) |
|
26 | ) | |
22 |
|
27 | |||
23 |
delay = models. |
|
28 | delay = models.IntegerField( | |
24 | verbose_name='Delay', |
|
29 | verbose_name='Delay', | |
25 | blank=False, |
|
30 | blank=False, | |
26 | null=False |
|
31 | null=False | |
27 | ) |
|
32 | ) | |
28 |
|
33 | |||
29 |
width = models. |
|
34 | width = models.IntegerField( | |
30 | verbose_name='Width', |
|
35 | verbose_name='Width', | |
31 | blank=False, |
|
36 | blank=False, | |
32 | null=False |
|
37 | null=False | |
33 | ) |
|
38 | ) | |
34 |
|
39 | |||
35 |
e |
|
40 | selector = models.IntegerField( | |
36 |
verbose_name=' |
|
41 | verbose_name='Selector', | |
|
42 | choices=SELECTOR_VALUE, | |||
37 | blank=False, |
|
43 | blank=False, | |
38 | null=False |
|
44 | null=False | |
39 | ) |
|
45 | ) | |
40 |
|
46 | |||
41 | class Meta: |
|
47 | class Meta: | |
42 | db_table = 'generator_configurations' |
|
48 | db_table = 'generator_configurations' | |
43 |
|
49 | |||
44 | def __str__(self): |
|
50 | def __str__(self): | |
45 | return str(self.label) |
|
51 | return str(self.label) | |
46 |
|
52 | |||
47 | def get_absolute_url_plot(self): |
|
53 | def get_absolute_url_plot(self): | |
48 | return reverse('url_plot_generator_pulses', args=[str(self.id)]) |
|
54 | return reverse('url_plot_generator_pulses', args=[str(self.id)]) | |
49 |
|
55 | |||
50 | def request(self, cmd, method='get', **kwargs): |
|
56 | def request(self, cmd, method='get', **kwargs): | |
51 |
|
57 | |||
52 | req = getattr(requests, method)(self.device.url(cmd), **kwargs) |
|
58 | req = getattr(requests, method)(self.device.url(cmd), **kwargs) | |
53 | payload = req.json() |
|
59 | payload = req.json() | |
54 |
|
60 | |||
55 | return payload |
|
61 | return payload | |
56 |
|
62 | |||
57 | def status_device(self): |
|
63 | def status_device(self): | |
58 |
|
64 | |||
59 | try: |
|
65 | try: | |
60 | self.device.status = 0 |
|
66 | #self.device.status = 0 | |
61 | payload = self.request('status') |
|
67 | #payload = self.request('status') | |
62 | if payload['status']=='enable': |
|
68 | payload = requests.get(self.device.url()) | |
63 | self.device.status = 3 |
|
69 | print(payload) | |
|
70 | if payload: | |||
|
71 | self.device.status = 1 | |||
64 | elif payload['status']=='disable': |
|
72 | elif payload['status']=='disable': | |
65 | self.device.status = 2 |
|
73 | self.device.status = 2 | |
66 | else: |
|
74 | else: | |
67 | self.device.status = 1 |
|
75 | self.device.status = 1 | |
68 | self.device.save() |
|
76 | self.device.save() | |
69 | self.message = 'Generator status: {}'.format(payload['status']) |
|
77 | self.message = 'Generator status: {}'.format(payload['status']) | |
70 | return False |
|
78 | return False | |
71 | except Exception as e: |
|
79 | except Exception as e: | |
72 | if 'No route to host' not in str(e): |
|
80 | if 'No route to host' not in str(e): | |
73 | self.device.status = 4 |
|
81 | self.device.status = 4 | |
74 | self.device.save() |
|
82 | self.device.save() | |
75 | self.message = 'Generator status: {}'.format(str(e)) |
|
83 | self.message = 'Generator status: {}'.format(str(e)) | |
76 | return False |
|
84 | return False | |
77 |
|
85 | |||
78 | self.device.save() |
|
86 | self.device.save() | |
79 | return True |
|
87 | return True | |
80 |
|
88 | |||
81 | def reset_device(self): |
|
89 | def reset_device(self): | |
82 |
|
90 | |||
83 | try: |
|
91 | try: | |
84 | payload = self.request('reset', 'post') |
|
92 | payload = self.request('reset', 'post') | |
85 | if payload['reset']=='ok': |
|
93 | if payload['reset']=='ok': | |
86 | self.message = 'Generator restarted OK' |
|
94 | self.message = 'Generator restarted OK' | |
87 | self.device.status = 2 |
|
95 | self.device.status = 2 | |
88 | self.device.save() |
|
96 | self.device.save() | |
89 | else: |
|
97 | else: | |
90 | self.message = 'Generator restart fail' |
|
98 | self.message = 'Generator restart fail' | |
91 | self.device.status = 4 |
|
99 | self.device.status = 4 | |
92 | self.device.save() |
|
100 | self.device.save() | |
93 | except Exception as e: |
|
101 | except Exception as e: | |
94 | self.message = 'Generator reset: {}'.format(str(e)) |
|
102 | self.message = 'Generator reset: {}'.format(str(e)) | |
95 | return False |
|
103 | return False | |
96 |
|
104 | |||
97 | return True |
|
105 | return True | |
98 |
|
106 | |||
99 | def stop_device(self): |
|
107 | def stop_device(self): | |
100 |
|
108 | |||
101 | try: |
|
109 | try: | |
102 | command = self.device.url() + "stop" |
|
110 | command = self.device.url() + "stop" | |
103 | r = requests.get(command) |
|
111 | r = requests.get(command) | |
104 | if r: |
|
112 | if r: | |
105 | self.device.status = 4 |
|
113 | self.device.status = 4 | |
106 | self.device.save() |
|
114 | self.device.save() | |
107 | self.message = 'Generator stopped' |
|
115 | self.message = 'Generator stopped' | |
108 | else: |
|
116 | else: | |
109 | self.device.status = 4 |
|
117 | self.device.status = 4 | |
110 | self.device.save() |
|
118 | self.device.save() | |
111 | return False |
|
119 | return False | |
112 | except Exception as e: |
|
120 | except Exception as e: | |
113 | if 'No route to host' not in str(e): |
|
121 | if 'No route to host' not in str(e): | |
114 | self.device.status = 4 |
|
122 | self.device.status = 4 | |
115 | else: |
|
123 | else: | |
116 | self.device.status = 0 |
|
124 | self.device.status = 0 | |
117 | self.message = 'Generator stop: {}'.format(str(e)) |
|
125 | #self.message = 'Generator stop: {}'.format(str(e)) | |
|
126 | self.message = "Generator can't start, please check network/device connection or IP address/port configuration" | |||
118 | self.device.save() |
|
127 | self.device.save() | |
119 | return False |
|
128 | return False | |
120 |
|
129 | |||
121 | return True |
|
130 | return True | |
122 |
|
131 | |||
123 | def start_device(self): |
|
132 | def start_device(self): | |
124 | print("Entró al start") |
|
133 | ||
125 | try: |
|
134 | try: | |
126 | generator = GeneratorConfiguration.objects.get(pk=self) |
|
135 | generator = GeneratorConfiguration.objects.get(pk=self) | |
127 | print(generator) |
|
136 | print(generator) | |
128 |
json_trmode = json.dumps({"periode": generator.periode, "delay": generator.delay, "width": generator.width, "e |
|
137 | json_trmode = json.dumps({"periode": generator.periode, "delay": generator.delay, "width": generator.width, "selector": generator.selector}) | |
|
138 | print(json_trmode) | |||
129 | base64_trmode = base64.urlsafe_b64encode(json_trmode.encode('ascii')) |
|
139 | base64_trmode = base64.urlsafe_b64encode(json_trmode.encode('ascii')) | |
130 | print(base64_trmode) |
|
140 | print(base64_trmode) | |
131 | trmode_url = self.device.url() + "trmode?params=" |
|
141 | trmode_url = self.device.url() + "trmode?params=" | |
132 | complete_url_trmode = trmode_url + base64_trmode.decode('ascii') |
|
142 | complete_url_trmode = trmode_url + base64_trmode.decode('ascii') | |
133 | print(complete_url_trmode) |
|
143 | print(complete_url_trmode) | |
134 | r = requests.get(complete_url_trmode) |
|
144 | r = requests.get(complete_url_trmode) | |
135 |
|
145 | |||
136 | if r: |
|
146 | if r: | |
137 | self.device.status = 3 |
|
147 | self.device.status = 3 | |
138 | self.device.save() |
|
148 | self.device.save() | |
139 | self.message = 'Generator configured and started' |
|
149 | self.message = 'Generator configured and started' | |
140 | else: |
|
150 | else: | |
141 | return False |
|
151 | return False | |
142 | except Exception as e: |
|
152 | except Exception as e: | |
143 | if 'No route to host' not in str(e): |
|
153 | if 'No route to host' not in str(e): | |
144 | self.device.status = 4 |
|
154 | self.device.status = 4 | |
145 | else: |
|
155 | else: | |
146 | self.device.status = 0 |
|
156 | self.device.status = 0 | |
147 | self.message = 'Generator start: {}'.format(str(e)) |
|
157 | #self.message = 'Generator start: {}'.format(str(e)) | |
|
158 | self.message = "Generator can't start, please check network/device connection or IP address/port configuration" | |||
148 | self.device.save() |
|
159 | self.device.save() | |
149 | return False |
|
160 | return False | |
150 |
|
161 | |||
151 | return True |
|
162 | return True | |
152 |
|
163 | |||
153 | #def write_device(self, raw=False): |
|
164 | #def write_device(self, raw=False): | |
154 |
|
165 | |||
155 | if not raw: |
|
166 | if not raw: | |
156 | clock = RCClock.objects.get(rc_configuration=self) |
|
167 | clock = RCClock.objects.get(rc_configuration=self) | |
157 | print(clock) |
|
168 | print(clock) | |
158 | if clock.mode: |
|
169 | if clock.mode: | |
159 | data = {'default': clock.frequency} |
|
170 | data = {'default': clock.frequency} | |
160 | else: |
|
171 | else: | |
161 | data = {'manual': [clock.multiplier, clock.divisor, clock.reference]} |
|
172 | data = {'manual': [clock.multiplier, clock.divisor, clock.reference]} | |
162 | payload = self.request('setfreq', 'post', data=json.dumps(data)) |
|
173 | payload = self.request('setfreq', 'post', data=json.dumps(data)) | |
163 | print(payload) |
|
174 | print(payload) | |
164 | if payload['command'] != 'ok': |
|
175 | if payload['command'] != 'ok': | |
165 | self.message = 'Generator write: {}'.format(payload['command']) |
|
176 | self.message = 'Generator write: {}'.format(payload['command']) | |
166 | else: |
|
177 | else: | |
167 | self.message = payload['programming'] |
|
178 | self.message = payload['programming'] | |
168 | if payload['programming'] == 'fail': |
|
179 | if payload['programming'] == 'fail': | |
169 | self.message = 'Generator write: error programming CGS chip' |
|
180 | self.message = 'Generator write: error programming CGS chip' | |
170 |
|
181 | |||
171 | values = [] |
|
182 | values = [] | |
172 | for pulse, delay in zip(self.get_pulses(), self.get_delays()): |
|
183 | for pulse, delay in zip(self.get_pulses(), self.get_delays()): | |
173 | while delay>65536: |
|
184 | while delay>65536: | |
174 | values.append((pulse, 65535)) |
|
185 | values.append((pulse, 65535)) | |
175 | delay -= 65536 |
|
186 | delay -= 65536 | |
176 | values.append((pulse, delay-1)) |
|
187 | values.append((pulse, delay-1)) | |
177 | data = bytearray() |
|
188 | data = bytearray() | |
178 | #reset |
|
189 | #reset | |
179 | data.extend((128, 0)) |
|
190 | data.extend((128, 0)) | |
180 | #disable |
|
191 | #disable | |
181 | data.extend((129, 0)) |
|
192 | data.extend((129, 0)) | |
182 | #SW switch |
|
193 | #SW switch | |
183 | if self.control_sw: |
|
194 | if self.control_sw: | |
184 | data.extend((130, 2)) |
|
195 | data.extend((130, 2)) | |
185 | else: |
|
196 | else: | |
186 | data.extend((130, 0)) |
|
197 | data.extend((130, 0)) | |
187 | #divider |
|
198 | #divider | |
188 | data.extend((131, self.clock_divider-1)) |
|
199 | data.extend((131, self.clock_divider-1)) | |
189 | #enable writing |
|
200 | #enable writing | |
190 | data.extend((139, 62)) |
|
201 | data.extend((139, 62)) | |
191 |
|
202 | |||
192 | last = 0 |
|
203 | last = 0 | |
193 | for tup in values: |
|
204 | for tup in values: | |
194 | vals = pack('<HH', last^tup[0], tup[1]) |
|
205 | vals = pack('<HH', last^tup[0], tup[1]) | |
195 | last = tup[0] |
|
206 | last = tup[0] | |
196 | data.extend((133, vals[1], 132, vals[0], 133, vals[3], 132, vals[2])) |
|
207 | data.extend((133, vals[1], 132, vals[0], 133, vals[3], 132, vals[2])) | |
197 |
|
208 | |||
198 | #enable |
|
209 | #enable | |
199 | data.extend((129, 1)) |
|
210 | data.extend((129, 1)) | |
200 |
|
211 | |||
201 | if raw: |
|
212 | if raw: | |
202 | return b64encode(data) |
|
213 | return b64encode(data) | |
203 |
|
214 | |||
204 | try: |
|
215 | try: | |
205 | payload = self.request('stop', 'post') |
|
216 | payload = self.request('stop', 'post') | |
206 | payload = self.request('reset', 'post') |
|
217 | payload = self.request('reset', 'post') | |
207 | #payload = self.request('divider', 'post', data={'divider': self.clock_divider-1}) |
|
218 | #payload = self.request('divider', 'post', data={'divider': self.clock_divider-1}) | |
208 | #payload = self.request('write', 'post', data=b64encode(bytearray((139, 62))), timeout=20) |
|
219 | #payload = self.request('write', 'post', data=b64encode(bytearray((139, 62))), timeout=20) | |
209 | n = len(data) |
|
220 | n = len(data) | |
210 | x = 0 |
|
221 | x = 0 | |
211 | #while x < n: |
|
222 | #while x < n: | |
212 | payload = self.request('write', 'post', data=b64encode(data)) |
|
223 | payload = self.request('write', 'post', data=b64encode(data)) | |
213 | # x += 1024 |
|
224 | # x += 1024 | |
214 |
|
225 | |||
215 | if payload['write']=='ok': |
|
226 | if payload['write']=='ok': | |
216 | self.device.status = 3 |
|
227 | self.device.status = 3 | |
217 | self.device.save() |
|
228 | self.device.save() | |
218 | self.message = 'Generator configured and started' |
|
229 | self.message = 'Generator configured and started' | |
219 | else: |
|
230 | else: | |
220 | self.device.status = 1 |
|
231 | self.device.status = 1 | |
221 | self.device.save() |
|
232 | self.device.save() | |
222 | self.message = 'Generator write: {}'.format(payload['write']) |
|
233 | self.message = 'Generator write: {}'.format(payload['write']) | |
223 | return False |
|
234 | return False | |
224 |
|
235 | |||
225 | #payload = self.request('start', 'post') |
|
236 | #payload = self.request('start', 'post') | |
226 |
|
237 | |||
227 | except Exception as e: |
|
238 | except Exception as e: | |
228 | if 'No route to host' not in str(e): |
|
239 | if 'No route to host' not in str(e): | |
229 | self.device.status = 4 |
|
240 | self.device.status = 4 | |
230 | else: |
|
241 | else: | |
231 | self.device.status = 0 |
|
242 | self.device.status = 0 | |
232 | self.message = 'Generator write: {}'.format(str(e)) |
|
243 | self.message = 'Generator write: {}'.format(str(e)) | |
233 | self.device.save() |
|
244 | self.device.save() | |
234 | return False |
|
245 | return False | |
235 |
|
246 | |||
236 | return True |
|
247 | return True | |
237 |
|
248 | |||
238 |
|
249 | |||
239 | def get_absolute_url_import(self): |
|
250 | def get_absolute_url_import(self): | |
240 | return reverse('url_import_generator_conf', args=[str(self.id)]) |
|
251 | return reverse('url_import_generator_conf', args=[str(self.id)]) |
@@ -1,30 +1,30 | |||||
1 | {% extends "dev_conf.html" %} |
|
1 | {% extends "dev_conf.html" %} | |
2 | {% load static %} |
|
2 | {% load static %} | |
3 | {% load bootstrap4 %} |
|
3 | {% load bootstrap4 %} | |
4 | {% load main_tags %} |
|
4 | {% load main_tags %} | |
5 |
|
5 | |||
6 | {% block content-detail %} |
|
6 | {% block content-detail %} | |
7 |
|
7 | |||
8 |
<h2> |
|
8 | <h2>Generator</h2> | |
9 | <table class="table table-bordered"> |
|
9 | <table class="table table-bordered"> | |
10 | <tr> |
|
10 | <tr> | |
11 | <th>Status</th> |
|
11 | <th>Status</th> | |
12 | <td class="text-{{dev_conf.device.status_color}}"><strong>{{dev_conf.device.get_status_display}}</strong></td> |
|
12 | <td class="text-{{dev_conf.device.status_color}}"><strong>{{dev_conf.device.get_status_display}}</strong></td> | |
13 | </tr> |
|
13 | </tr> | |
14 |
|
14 | |||
15 | {% for key in dev_conf_keys %} |
|
15 | {% for key in dev_conf_keys %} | |
16 | <tr> |
|
16 | <tr> | |
17 | <th>{% get_verbose_field_name dev_conf key %}</th> |
|
17 | <th>{% get_verbose_field_name dev_conf key %}</th> | |
18 | <td>{{dev_conf|attr:key}}</td> |
|
18 | <td>{{dev_conf|attr:key}}</td> | |
19 | </tr> |
|
19 | </tr> | |
20 | {% endfor %} |
|
20 | {% endfor %} | |
21 | </table> |
|
21 | </table> | |
22 | {% endblock %} |
|
22 | {% endblock %} | |
23 |
|
23 | |||
24 | {% block extra-js%} |
|
24 | {% block extra-js%} | |
25 | <script type="text/javascript"> |
|
25 | <script type="text/javascript"> | |
26 | $("#bt_toggle").click(function() { |
|
26 | $("#bt_toggle").click(function() { | |
27 | $(".panel-collapse").collapse('toggle') |
|
27 | $(".panel-collapse").collapse('toggle') | |
28 | }); |
|
28 | }); | |
29 | </script> |
|
29 | </script> | |
30 | {% endblock %} No newline at end of file |
|
30 | {% endblock %} |
@@ -1,27 +1,27 | |||||
1 | {% extends "dev_conf_edit.html" %} |
|
1 | {% extends "dev_conf_edit.html" %} | |
2 | {% load bootstrap4 %} |
|
2 | {% load bootstrap4 %} | |
3 | {% load static %} |
|
3 | {% load static %} | |
4 |
|
4 | |||
5 | {% block extra-head %} |
|
5 | {% block extra-head %} | |
6 | <style type="text/css"> |
|
6 | <style type="text/css"> | |
7 | /* show the move cursor as the user moves the mouse over the panel header.*/ |
|
7 | /* show the move cursor as the user moves the mouse over the panel header.*/ | |
8 | .panel-default { cursor: move; } |
|
8 | .panel-default { cursor: move; } | |
9 | </style> |
|
9 | </style> | |
10 | <script src="{% static 'js/jquery-ui.min.js' %}"></script> |
|
10 | <script src="{% static 'js/jquery-ui.min.js' %}"></script> | |
11 |
|
11 | |||
12 | {% endblock %} |
|
12 | {% endblock %} | |
13 |
|
13 | |||
14 | {% block content %} |
|
14 | {% block content %} | |
15 | <form class="form" method="post"> |
|
15 | <form class="form" method="post"> | |
16 | {% csrf_token %} |
|
16 | {% csrf_token %} | |
17 |
<h2> |
|
17 | <h2>Generator</h2> | |
18 | {% bootstrap_form form layout='horizontal' size='medium' %} |
|
18 | {% bootstrap_form form layout='horizontal' size='medium' %} | |
19 | <div style="clear: both;"></div> |
|
19 | <div style="clear: both;"></div> | |
20 | <br> |
|
20 | <br> | |
21 | <div class="pull-right"> |
|
21 | <div class="pull-right"> | |
22 | <button type="button" class="btn btn-primary" onclick="{% if previous %}window.location.replace('{{ previous }}');{% else %}history.go(-1);{% endif %}">Cancel</button> |
|
22 | <button type="button" class="btn btn-primary" onclick="{% if previous %}window.location.replace('{{ previous }}');{% else %}history.go(-1);{% endif %}">Cancel</button> | |
23 | <button type="submit" class="btn btn-primary">{{button}}</button> |
|
23 | <button type="submit" class="btn btn-primary">{{button}}</button> | |
24 | </div> |
|
24 | </div> | |
25 | </form> |
|
25 | </form> | |
26 | {% endblock %} |
|
26 | {% endblock %} | |
27 | No newline at end of file |
|
27 |
@@ -1,136 +1,139 | |||||
1 |
|
1 | |||
2 | import json |
|
2 | import json | |
3 |
|
3 | |||
4 | from django.contrib import messages |
|
4 | from django.contrib import messages | |
5 | from django.utils.safestring import mark_safe |
|
5 | from django.utils.safestring import mark_safe | |
6 | from django.shortcuts import render, redirect, get_object_or_404, HttpResponse |
|
6 | from django.shortcuts import render, redirect, get_object_or_404, HttpResponse | |
7 | from django.contrib.auth.decorators import login_required |
|
7 | from django.contrib.auth.decorators import login_required | |
8 |
|
8 | |||
9 | from apps.main.models import Experiment, Device |
|
9 | from apps.main.models import Experiment, Device | |
10 | from apps.main.views import sidebar |
|
10 | from apps.main.views import sidebar | |
11 |
|
11 | |||
12 | from .models import GeneratorConfiguration |
|
12 | from .models import GeneratorConfiguration | |
13 | from .forms import GeneratorConfigurationForm, GeneratorImportForm |
|
13 | from .forms import GeneratorConfigurationForm, GeneratorImportForm | |
14 |
|
14 | |||
15 |
|
15 | |||
16 | def conf(request, conf_id): |
|
16 | def conf(request, conf_id): | |
17 |
|
17 | |||
18 | conf = get_object_or_404(GeneratorConfiguration, pk=conf_id) |
|
18 | conf = get_object_or_404(GeneratorConfiguration, pk=conf_id) | |
19 |
|
19 | |||
20 | kwargs = {} |
|
20 | kwargs = {} | |
21 | kwargs['dev_conf'] = conf |
|
21 | kwargs['dev_conf'] = conf | |
22 | kwargs['dev_conf_keys'] = ['periode', 'delay', 'width'] |
|
22 | kwargs['dev_conf_keys'] = ['periode', 'delay', 'width', 'selector'] | |
23 |
|
23 | |||
24 | kwargs['title'] = 'Configuration' |
|
24 | kwargs['title'] = 'Configuration' | |
25 | kwargs['suptitle'] = 'Detail' |
|
25 | kwargs['suptitle'] = 'Detail' | |
26 |
|
26 | |||
27 | kwargs['button'] = 'Edit Configuration' |
|
27 | kwargs['button'] = 'Edit Configuration' | |
|
28 | ||||
|
29 | conf.status_device() | |||
|
30 | ||||
28 | ###### SIDEBAR ###### |
|
31 | ###### SIDEBAR ###### | |
29 | kwargs.update(sidebar(conf=conf)) |
|
32 | kwargs.update(sidebar(conf=conf)) | |
30 |
|
33 | |||
31 | return render(request, 'generator_conf.html', kwargs) |
|
34 | return render(request, 'generator_conf.html', kwargs) | |
32 |
|
35 | |||
33 | @login_required |
|
36 | @login_required | |
34 | def conf_edit(request, conf_id): |
|
37 | def conf_edit(request, conf_id): | |
35 |
|
38 | |||
36 | conf = get_object_or_404(GeneratorConfiguration, pk=conf_id) |
|
39 | conf = get_object_or_404(GeneratorConfiguration, pk=conf_id) | |
37 | print(conf) |
|
40 | print(conf) | |
38 | #print("fin de carga de params") |
|
41 | #print("fin de carga de params") | |
39 | if request.method=='GET': |
|
42 | if request.method=='GET': | |
40 | print("GET case") |
|
43 | print("GET case") | |
41 | form = GeneratorConfigurationForm(instance=conf) |
|
44 | form = GeneratorConfigurationForm(instance=conf) | |
42 | print(form) |
|
45 | print(form) | |
43 |
|
46 | |||
44 | elif request.method=='POST': |
|
47 | elif request.method=='POST': | |
45 | #print("ingreso a post conf edit") |
|
48 | #print("ingreso a post conf edit") | |
46 | line_data = {} |
|
49 | line_data = {} | |
47 | conf_data = {} |
|
50 | conf_data = {} | |
48 | clock_data = {} |
|
51 | clock_data = {} | |
49 | extras = [] |
|
52 | extras = [] | |
50 | print("Inicio impresion POST#####") |
|
53 | print("Inicio impresion POST#####") | |
51 | print(request.POST.items) |
|
54 | print(request.POST.items) | |
52 | print("Fin impresion de POST items#####") |
|
55 | print("Fin impresion de POST items#####") | |
53 | #classified post fields |
|
56 | #classified post fields | |
54 | for label,value in request.POST.items(): |
|
57 | for label,value in request.POST.items(): | |
55 | if label=='csrfmiddlewaretoken': |
|
58 | if label=='csrfmiddlewaretoken': | |
56 | continue |
|
59 | continue | |
57 |
|
60 | |||
58 | if label.count('|')==0: |
|
61 | if label.count('|')==0: | |
59 | if label in ('mode', 'multiplier', 'divisor', 'reference', 'frequency'): |
|
62 | if label in ('mode', 'multiplier', 'divisor', 'reference', 'frequency'): | |
60 | clock_data[label] = value |
|
63 | clock_data[label] = value | |
61 | else: |
|
64 | else: | |
62 | conf_data[label] = value |
|
65 | conf_data[label] = value | |
63 | continue |
|
66 | continue | |
64 |
|
67 | |||
65 | elif label.split('|')[0]!='-1': |
|
68 | elif label.split('|')[0]!='-1': | |
66 | extras.append(label) |
|
69 | extras.append(label) | |
67 | continue |
|
70 | continue | |
68 |
|
71 | |||
69 | #print(label) |
|
72 | #print(label) | |
70 | x, pk, name = label.split('|') |
|
73 | x, pk, name = label.split('|') | |
71 |
|
74 | |||
72 | if name=='codes': |
|
75 | if name=='codes': | |
73 | value = [s for s in value.split('\r\n') if s] |
|
76 | value = [s for s in value.split('\r\n') if s] | |
74 |
|
77 | |||
75 | if pk in line_data: |
|
78 | if pk in line_data: | |
76 | line_data[pk][name] = value |
|
79 | line_data[pk][name] = value | |
77 | else: |
|
80 | else: | |
78 | line_data[pk] = {name:value} |
|
81 | line_data[pk] = {name:value} | |
79 | #print(line_data[pk]) |
|
82 | #print(line_data[pk]) | |
80 | #update conf |
|
83 | #update conf | |
81 |
|
84 | |||
82 | form = GeneratorConfigurationForm(conf_data, instance=conf) |
|
85 | form = GeneratorConfigurationForm(conf_data, instance=conf) | |
83 |
|
86 | |||
84 | #print(request.POST.items()) |
|
87 | #print(request.POST.items()) | |
85 |
|
88 | |||
86 | if form.is_valid(): |
|
89 | if form.is_valid(): | |
87 | form.save() |
|
90 | form.save() | |
88 |
|
91 | |||
89 | messages.success(request, 'Generator configuration successfully updated') |
|
92 | messages.success(request, 'Generator configuration successfully updated') | |
90 |
|
93 | |||
91 | return redirect(conf.get_absolute_url()) |
|
94 | return redirect(conf.get_absolute_url()) | |
92 |
|
95 | |||
93 | kwargs = {} |
|
96 | kwargs = {} | |
94 | kwargs['dev_conf'] = conf |
|
97 | kwargs['dev_conf'] = conf | |
95 | kwargs['form'] = form |
|
98 | kwargs['form'] = form | |
96 | kwargs['edit'] = True |
|
99 | kwargs['edit'] = True | |
97 |
|
100 | |||
98 | kwargs['title'] = 'Generator Configuration' |
|
101 | kwargs['title'] = 'Generator Configuration' | |
99 | kwargs['suptitle'] = 'Edit' |
|
102 | kwargs['suptitle'] = 'Edit' | |
100 | kwargs['button'] = 'Update' |
|
103 | kwargs['button'] = 'Update' | |
101 |
|
104 | |||
102 | print(kwargs) |
|
105 | print(kwargs) | |
103 | print(form) |
|
106 | print(form) | |
104 | return render(request, 'generator_conf_edit.html', kwargs) |
|
107 | return render(request, 'generator_conf_edit.html', kwargs) | |
105 |
|
108 | |||
106 | def import_file(request, conf_id): |
|
109 | def import_file(request, conf_id): | |
107 |
|
110 | |||
108 | conf = get_object_or_404(GeneratorConfiguration, pk=conf_id) |
|
111 | conf = get_object_or_404(GeneratorConfiguration, pk=conf_id) | |
109 | if request.method=='POST': |
|
112 | if request.method=='POST': | |
110 | form = GeneratorImportForm(request.POST, request.FILES) |
|
113 | form = GeneratorImportForm(request.POST, request.FILES) | |
111 | if form.is_valid(): |
|
114 | if form.is_valid(): | |
112 | try: |
|
115 | try: | |
113 | data = conf.import_from_file(request.FILES['file_name']) |
|
116 | data = conf.import_from_file(request.FILES['file_name']) | |
114 | conf.dict_to_parms(data) |
|
117 | conf.dict_to_parms(data) | |
115 | messages.success(request, 'Configuration "%s" loaded succesfully' % request.FILES['file_name']) |
|
118 | messages.success(request, 'Configuration "%s" loaded succesfully' % request.FILES['file_name']) | |
116 | return redirect(conf.get_absolute_url_edit()) |
|
119 | return redirect(conf.get_absolute_url_edit()) | |
117 |
|
120 | |||
118 | except Exception as e: |
|
121 | except Exception as e: | |
119 | messages.error(request, 'Error parsing file: "%s" - %s' % (request.FILES['file_name'], repr(e))) |
|
122 | messages.error(request, 'Error parsing file: "%s" - %s' % (request.FILES['file_name'], repr(e))) | |
120 | else: |
|
123 | else: | |
121 | messages.warning(request, 'Your current configuration will be replaced') |
|
124 | messages.warning(request, 'Your current configuration will be replaced') | |
122 | form = GeneratorImportForm() |
|
125 | form = GeneratorImportForm() | |
123 |
|
126 | |||
124 | kwargs = {} |
|
127 | kwargs = {} | |
125 | kwargs['form'] = form |
|
128 | kwargs['form'] = form | |
126 | kwargs['title'] = 'Generator Configuration' |
|
129 | kwargs['title'] = 'Generator Configuration' | |
127 | kwargs['suptitle'] = 'Import file' |
|
130 | kwargs['suptitle'] = 'Import file' | |
128 | kwargs['button'] = 'Upload' |
|
131 | kwargs['button'] = 'Upload' | |
129 | kwargs['previous'] = conf.get_absolute_url() |
|
132 | kwargs['previous'] = conf.get_absolute_url() | |
130 |
|
133 | |||
131 | return render(request, 'generator_import.html', kwargs) |
|
134 | return render(request, 'generator_import.html', kwargs) | |
132 |
|
135 | |||
133 | def conf_raw(request, conf_id): |
|
136 | def conf_raw(request, conf_id): | |
134 | conf = get_object_or_404(GeneratorConfiguration, pk=conf_id) |
|
137 | conf = get_object_or_404(GeneratorConfiguration, pk=conf_id) | |
135 | raw = conf.write_device(raw=True) |
|
138 | raw = conf.write_device(raw=True) | |
136 | return HttpResponse(raw, content_type='application/json') No newline at end of file |
|
139 | return HttpResponse(raw, content_type='application/json') |
@@ -1,731 +1,733 | |||||
1 |
|
1 | |||
2 | import os |
|
2 | import os | |
3 | import json |
|
3 | import json | |
4 | import requests |
|
4 | import requests | |
5 | import time |
|
5 | import time | |
6 | from datetime import datetime |
|
6 | from datetime import datetime | |
7 |
|
7 | |||
8 | try: |
|
8 | try: | |
9 | from polymorphic.models import PolymorphicModel |
|
9 | from polymorphic.models import PolymorphicModel | |
10 | except: |
|
10 | except: | |
11 | from polymorphic import PolymorphicModel |
|
11 | from polymorphic import PolymorphicModel | |
12 |
|
12 | |||
13 | from django.template.base import kwarg_re |
|
13 | from django.template.base import kwarg_re | |
14 | from django.db import models |
|
14 | from django.db import models | |
15 | from django.urls import reverse |
|
15 | from django.urls import reverse | |
16 | from django.core.validators import MinValueValidator, MaxValueValidator |
|
16 | from django.core.validators import MinValueValidator, MaxValueValidator | |
17 | from django.shortcuts import get_object_or_404 |
|
17 | from django.shortcuts import get_object_or_404 | |
18 | from django.contrib.auth.models import User |
|
18 | from django.contrib.auth.models import User | |
19 | from django.db.models.signals import post_save |
|
19 | from django.db.models.signals import post_save | |
20 | from django.dispatch import receiver |
|
20 | from django.dispatch import receiver | |
21 |
|
21 | |||
22 | from apps.main.utils import Params |
|
22 | from apps.main.utils import Params | |
23 |
|
23 | |||
24 |
|
24 | |||
25 | DEV_PORTS = { |
|
25 | DEV_PORTS = { | |
26 | 'pedestal' : 80, |
|
26 | 'pedestal' : 80, | |
27 |
' |
|
27 | 'pedestal_dev' : 80, | |
28 |
' |
|
28 | 'generator' : 80, | |
29 |
'usrp_ |
|
29 | 'usrp_rx' : 2000, | |
|
30 | 'usrp_tx' : 2000, | |||
30 | } |
|
31 | } | |
31 |
|
32 | |||
32 | RADAR_STATES = ( |
|
33 | RADAR_STATES = ( | |
33 | (0, 'No connected'), |
|
34 | (0, 'No connected'), | |
34 | (1, 'Connected'), |
|
35 | (1, 'Connected'), | |
35 | (2, 'Configured'), |
|
36 | (2, 'Configured'), | |
36 | (3, 'Running'), |
|
37 | (3, 'Running'), | |
37 | (4, 'Scheduled'), |
|
38 | (4, 'Scheduled'), | |
38 | ) |
|
39 | ) | |
39 |
|
40 | |||
40 | EXPERIMENT_TYPE = ( |
|
41 | EXPERIMENT_TYPE = ( | |
41 | (0, 'RAW_DATA'), |
|
42 | (0, 'RAW_DATA'), | |
42 | (1, 'PDATA'), |
|
43 | (1, 'PDATA'), | |
43 | ) |
|
44 | ) | |
44 |
|
45 | |||
45 | DECODE_TYPE = ( |
|
46 | DECODE_TYPE = ( | |
46 | (0, 'None'), |
|
47 | (0, 'None'), | |
47 | (1, 'TimeDomain'), |
|
48 | (1, 'TimeDomain'), | |
48 | (2, 'FreqDomain'), |
|
49 | (2, 'FreqDomain'), | |
49 | (3, 'InvFreqDomain'), |
|
50 | (3, 'InvFreqDomain'), | |
50 | ) |
|
51 | ) | |
51 |
|
52 | |||
52 | DEV_STATES = ( |
|
53 | DEV_STATES = ( | |
53 |
(0, ' |
|
54 | (0, 'Unknown'), | |
54 | (1, 'Connected'), |
|
55 | (1, 'Connected'), | |
55 | (2, 'Configured'), |
|
56 | (2, 'Configured'), | |
56 | (3, 'Running'), |
|
57 | (3, 'Running'), | |
57 |
(4, ' |
|
58 | (4, 'Offline'), | |
58 | ) |
|
59 | ) | |
59 |
|
60 | |||
60 | DEV_TYPES = ( |
|
61 | DEV_TYPES = ( | |
61 | ('', 'Select a device type'), |
|
62 | ('', 'Select a device type'), | |
62 | ('pedestal', 'Pedestal Controller'), |
|
63 | ('pedestal', 'Pedestal Controller'), | |
|
64 | ('pedestal_dev', 'Pedestal Controller Dev Mode'), | |||
63 | ('generator', 'Pulse Generator'), |
|
65 | ('generator', 'Pulse Generator'), | |
64 | ('usrp_rx', 'Universal Software Radio Peripheral Rx'), |
|
66 | ('usrp_rx', 'Universal Software Radio Peripheral Rx'), | |
65 | ('usrp_tx', 'Universal Software Radio Peripheral Tx'), |
|
67 | ('usrp_tx', 'Universal Software Radio Peripheral Tx'), | |
66 | ) |
|
68 | ) | |
67 |
|
69 | |||
68 | EXP_STATES = ( |
|
70 | EXP_STATES = ( | |
69 | (0,'Error'), #RED |
|
71 | (0,'Error'), #RED | |
70 | (1,'Cancelled'), #YELLOW |
|
72 | (1,'Cancelled'), #YELLOW | |
71 | (2,'Running'), #GREEN |
|
73 | (2,'Running'), #GREEN | |
72 | (3,'Scheduled'), #BLUE |
|
74 | (3,'Scheduled'), #BLUE | |
73 | (4,'Unknown'), #WHITE |
|
75 | (4,'Unknown'), #WHITE | |
74 | ) |
|
76 | ) | |
75 |
|
77 | |||
76 | CONF_TYPES = ( |
|
78 | CONF_TYPES = ( | |
77 | (0, 'Active'), |
|
79 | (0, 'Active'), | |
78 | (1, 'Historical'), |
|
80 | (1, 'Historical'), | |
79 | ) |
|
81 | ) | |
80 |
|
82 | |||
81 | class Profile(models.Model): |
|
83 | class Profile(models.Model): | |
82 | user = models.OneToOneField(User, on_delete=models.CASCADE) |
|
84 | user = models.OneToOneField(User, on_delete=models.CASCADE) | |
83 | theme = models.CharField(max_length=30, default='spacelab') |
|
85 | theme = models.CharField(max_length=30, default='spacelab') | |
84 |
|
86 | |||
85 |
|
87 | |||
86 | @receiver(post_save, sender=User) |
|
88 | @receiver(post_save, sender=User) | |
87 | def create_user_profile(sender, instance, created, **kwargs): |
|
89 | def create_user_profile(sender, instance, created, **kwargs): | |
88 | if created: |
|
90 | if created: | |
89 | Profile.objects.create(user=instance) |
|
91 | Profile.objects.create(user=instance) | |
90 |
|
92 | |||
91 | @receiver(post_save, sender=User) |
|
93 | @receiver(post_save, sender=User) | |
92 | def save_user_profile(sender, instance, **kwargs): |
|
94 | def save_user_profile(sender, instance, **kwargs): | |
93 | instance.profile.save() |
|
95 | instance.profile.save() | |
94 |
|
96 | |||
95 |
|
97 | |||
96 | class Location(models.Model): |
|
98 | class Location(models.Model): | |
97 |
|
99 | |||
98 | name = models.CharField(max_length = 30) |
|
100 | name = models.CharField(max_length = 30) | |
99 | description = models.TextField(blank=True, null=True) |
|
101 | description = models.TextField(blank=True, null=True) | |
100 |
|
102 | |||
101 | class Meta: |
|
103 | class Meta: | |
102 | db_table = 'db_location' |
|
104 | db_table = 'db_location' | |
103 |
|
105 | |||
104 | def __str__(self): |
|
106 | def __str__(self): | |
105 | return u'%s' % self.name |
|
107 | return u'%s' % self.name | |
106 |
|
108 | |||
107 | def get_absolute_url(self): |
|
109 | def get_absolute_url(self): | |
108 | return reverse('url_location', args=[str(self.id)]) |
|
110 | return reverse('url_location', args=[str(self.id)]) | |
109 |
|
111 | |||
110 |
|
112 | |||
111 | class DeviceType(models.Model): |
|
113 | class DeviceType(models.Model): | |
112 |
|
114 | |||
113 |
name = models.CharField(max_length = 1 |
|
115 | name = models.CharField(max_length = 15, choices = DEV_TYPES, default = 'pedestal') | |
114 | sequence = models.PositiveSmallIntegerField(default=55) |
|
116 | sequence = models.PositiveSmallIntegerField(default=55) | |
115 | description = models.TextField(blank=True, null=True) |
|
117 | description = models.TextField(blank=True, null=True) | |
116 |
|
118 | |||
117 | class Meta: |
|
119 | class Meta: | |
118 | db_table = 'db_device_types' |
|
120 | db_table = 'db_device_types' | |
119 |
|
121 | |||
120 | def __str__(self): |
|
122 | def __str__(self): | |
121 | return u'%s' % self.name.title() |
|
123 | return u'%s' % self.name.title() | |
122 |
|
124 | |||
123 | class Device(models.Model): |
|
125 | class Device(models.Model): | |
124 |
|
126 | |||
125 | device_type = models.ForeignKey('DeviceType', on_delete=models.CASCADE) |
|
127 | device_type = models.ForeignKey('DeviceType', on_delete=models.CASCADE) | |
126 | location = models.ForeignKey('Location', on_delete=models.CASCADE) |
|
128 | location = models.ForeignKey('Location', on_delete=models.CASCADE) | |
127 | ip_address = models.GenericIPAddressField(verbose_name = 'IP address', protocol='IPv4', default='0.0.0.0') |
|
129 | ip_address = models.GenericIPAddressField(verbose_name = 'IP address', protocol='IPv4', default='0.0.0.0') | |
128 | port_address = models.PositiveSmallIntegerField(default=2000) |
|
130 | port_address = models.PositiveSmallIntegerField(default=2000) | |
129 | description = models.TextField(blank=True, null=True) |
|
131 | description = models.TextField(blank=True, null=True) | |
130 | status = models.PositiveSmallIntegerField(default=4, choices=DEV_STATES) |
|
132 | status = models.PositiveSmallIntegerField(default=4, choices=DEV_STATES) | |
131 | conf_active = models.PositiveIntegerField(default=0, verbose_name='Current configuration') |
|
133 | conf_active = models.PositiveIntegerField(default=0, verbose_name='Current configuration') | |
132 |
|
134 | |||
133 | class Meta: |
|
135 | class Meta: | |
134 | db_table = 'db_devices' |
|
136 | db_table = 'db_devices' | |
135 |
|
137 | |||
136 | def __str__(self): |
|
138 | def __str__(self): | |
137 | ret = self.device_type |
|
139 | ret = self.device_type | |
138 | #ret = u'{} [{}]'.format(self.device_type.name.upper(), self.location.name) |
|
140 | #ret = u'{} [{}]'.format(self.device_type.name.upper(), self.location.name) | |
139 | return str(ret) |
|
141 | return str(ret) | |
140 |
|
142 | |||
141 | @property |
|
143 | @property | |
142 | def name(self): |
|
144 | def name(self): | |
143 | return str(self) |
|
145 | return str(self) | |
144 |
|
146 | |||
145 | def get_status(self): |
|
147 | def get_status(self): | |
146 | return self.status |
|
148 | return self.status | |
147 |
|
149 | |||
148 | @property |
|
150 | @property | |
149 | def status_color(self): |
|
151 | def status_color(self): | |
150 | color = 'muted' |
|
152 | color = 'muted' | |
151 | if self.status == 0: |
|
153 | if self.status == 0: | |
152 | color = "danger" |
|
154 | color = "danger" | |
153 | elif self.status == 1: |
|
155 | elif self.status == 1: | |
154 | color = "warning" |
|
156 | color = "warning" | |
155 | elif self.status == 2: |
|
157 | elif self.status == 2: | |
156 | color = "info" |
|
158 | color = "info" | |
157 | elif self.status == 3: |
|
159 | elif self.status == 3: | |
158 | color = "success" |
|
160 | color = "success" | |
159 |
|
161 | |||
160 | return color |
|
162 | return color | |
161 |
|
163 | |||
162 | def url(self, path=None): |
|
164 | def url(self, path=None): | |
163 |
|
165 | |||
164 | if path: |
|
166 | if path: | |
165 | return 'http://{}:{}/{}/'.format(self.ip_address, self.port_address, path) |
|
167 | return 'http://{}:{}/{}/'.format(self.ip_address, self.port_address, path) | |
166 | else: |
|
168 | else: | |
167 | return 'http://{}:{}/'.format(self.ip_address, self.port_address) |
|
169 | return 'http://{}:{}/'.format(self.ip_address, self.port_address) | |
168 |
|
170 | |||
169 | def get_absolute_url(self): |
|
171 | def get_absolute_url(self): | |
170 | return reverse('url_device', args=[str(self.id)]) |
|
172 | return reverse('url_device', args=[str(self.id)]) | |
171 |
|
173 | |||
172 | def get_absolute_url_edit(self): |
|
174 | def get_absolute_url_edit(self): | |
173 | return reverse('url_edit_device', args=[str(self.id)]) |
|
175 | return reverse('url_edit_device', args=[str(self.id)]) | |
174 |
|
176 | |||
175 | def get_absolute_url_delete(self): |
|
177 | def get_absolute_url_delete(self): | |
176 | return reverse('url_delete_device', args=[str(self.id)]) |
|
178 | return reverse('url_delete_device', args=[str(self.id)]) | |
177 |
|
179 | |||
178 | def change_ip(self, ip_address, mask, gateway, dns, **kwargs): |
|
180 | def change_ip(self, ip_address, mask, gateway, dns, **kwargs): | |
179 |
|
181 | |||
180 | if self.device_type.name=='pedestal': |
|
182 | if self.device_type.name=='pedestal': | |
181 | headers = {'content-type': "application/json", |
|
183 | headers = {'content-type': "application/json", | |
182 | 'cache-control': "no-cache"} |
|
184 | 'cache-control': "no-cache"} | |
183 |
|
185 | |||
184 | ip = [int(x) for x in ip_address.split('.')] |
|
186 | ip = [int(x) for x in ip_address.split('.')] | |
185 | dns = [int(x) for x in dns.split('.')] |
|
187 | dns = [int(x) for x in dns.split('.')] | |
186 | gateway = [int(x) for x in gateway.split('.')] |
|
188 | gateway = [int(x) for x in gateway.split('.')] | |
187 | subnet = [int(x) for x in mask.split('.')] |
|
189 | subnet = [int(x) for x in mask.split('.')] | |
188 |
|
190 | |||
189 | payload = { |
|
191 | payload = { | |
190 | "ip": ip, |
|
192 | "ip": ip, | |
191 | "dns": dns, |
|
193 | "dns": dns, | |
192 | "gateway": gateway, |
|
194 | "gateway": gateway, | |
193 | "subnet": subnet |
|
195 | "subnet": subnet | |
194 | } |
|
196 | } | |
195 |
|
197 | |||
196 | req = requests.post(self.url('changeip'), data=json.dumps(payload), headers=headers) |
|
198 | req = requests.post(self.url('changeip'), data=json.dumps(payload), headers=headers) | |
197 | try: |
|
199 | try: | |
198 | answer = req.json() |
|
200 | answer = req.json() | |
199 | if answer['changeip']=='ok': |
|
201 | if answer['changeip']=='ok': | |
200 | self.message = '25|IP succesfully changed' |
|
202 | self.message = '25|IP succesfully changed' | |
201 | self.ip_address = ip_address |
|
203 | self.ip_address = ip_address | |
202 | self.save() |
|
204 | self.save() | |
203 | else: |
|
205 | else: | |
204 | self.message = '30|An error ocuur when changing IP' |
|
206 | self.message = '30|An error ocuur when changing IP' | |
205 | except Exception as e: |
|
207 | except Exception as e: | |
206 | self.message = '40|{}'.format(str(e)) |
|
208 | self.message = '40|{}'.format(str(e)) | |
207 | else: |
|
209 | else: | |
208 | self.message = 'Not implemented' |
|
210 | self.message = 'Not implemented' | |
209 | return False |
|
211 | return False | |
210 |
|
212 | |||
211 | return True |
|
213 | return True | |
212 |
|
214 | |||
213 |
|
215 | |||
214 | class Campaign(models.Model): |
|
216 | class Campaign(models.Model): | |
215 |
|
217 | |||
216 | template = models.BooleanField(default=False) |
|
218 | template = models.BooleanField(default=False) | |
217 | name = models.CharField(max_length=60, unique=True) |
|
219 | name = models.CharField(max_length=60, unique=True) | |
218 | start_date = models.DateTimeField(blank=True, null=True) |
|
220 | start_date = models.DateTimeField(blank=True, null=True) | |
219 | end_date = models.DateTimeField(blank=True, null=True) |
|
221 | end_date = models.DateTimeField(blank=True, null=True) | |
220 | tags = models.CharField(max_length=40, blank=True, null=True) |
|
222 | tags = models.CharField(max_length=40, blank=True, null=True) | |
221 | description = models.TextField(blank=True, null=True) |
|
223 | description = models.TextField(blank=True, null=True) | |
222 | experiments = models.ManyToManyField('Experiment', blank=True) |
|
224 | experiments = models.ManyToManyField('Experiment', blank=True) | |
223 | author = models.ForeignKey(User, null=True, blank=True,on_delete=models.CASCADE) |
|
225 | author = models.ForeignKey(User, null=True, blank=True,on_delete=models.CASCADE) | |
224 |
|
226 | |||
225 | class Meta: |
|
227 | class Meta: | |
226 | db_table = 'db_campaigns' |
|
228 | db_table = 'db_campaigns' | |
227 | ordering = ('name',) |
|
229 | ordering = ('name',) | |
228 |
|
230 | |||
229 | def __str__(self): |
|
231 | def __str__(self): | |
230 | if self.template: |
|
232 | if self.template: | |
231 | return u'{} (template)'.format(self.name) |
|
233 | return u'{} (template)'.format(self.name) | |
232 | else: |
|
234 | else: | |
233 | return u'{}'.format(self.name) |
|
235 | return u'{}'.format(self.name) | |
234 |
|
236 | |||
235 | def jsonify(self): |
|
237 | def jsonify(self): | |
236 |
|
238 | |||
237 | data = {} |
|
239 | data = {} | |
238 |
|
240 | |||
239 | ignored = ('template') |
|
241 | ignored = ('template') | |
240 |
|
242 | |||
241 | for field in self._meta.fields: |
|
243 | for field in self._meta.fields: | |
242 | if field.name in ignored: |
|
244 | if field.name in ignored: | |
243 | continue |
|
245 | continue | |
244 | data[field.name] = field.value_from_object(self) |
|
246 | data[field.name] = field.value_from_object(self) | |
245 |
|
247 | |||
246 | data['start_date'] = data['start_date'].strftime('%Y-%m-%d') |
|
248 | data['start_date'] = data['start_date'].strftime('%Y-%m-%d') | |
247 | data['end_date'] = data['end_date'].strftime('%Y-%m-%d') |
|
249 | data['end_date'] = data['end_date'].strftime('%Y-%m-%d') | |
248 |
|
250 | |||
249 | return data |
|
251 | return data | |
250 |
|
252 | |||
251 | def parms_to_dict(self): |
|
253 | def parms_to_dict(self): | |
252 |
|
254 | |||
253 | params = Params({}) |
|
255 | params = Params({}) | |
254 | params.add(self.jsonify(), 'campaigns') |
|
256 | params.add(self.jsonify(), 'campaigns') | |
255 |
|
257 | |||
256 | for exp in Experiment.objects.filter(campaign = self): |
|
258 | for exp in Experiment.objects.filter(campaign = self): | |
257 | params.add(exp.jsonify(), 'experiments') |
|
259 | params.add(exp.jsonify(), 'experiments') | |
258 | configurations = Configuration.objects.filter(experiment=exp, type=0) |
|
260 | configurations = Configuration.objects.filter(experiment=exp, type=0) | |
259 |
|
261 | |||
260 | for conf in configurations: |
|
262 | for conf in configurations: | |
261 | params.add(conf.jsonify(), 'configurations') |
|
263 | params.add(conf.jsonify(), 'configurations') | |
262 |
|
264 | |||
263 | return params.data |
|
265 | return params.data | |
264 |
|
266 | |||
265 | def dict_to_parms(self, parms, CONF_MODELS): |
|
267 | def dict_to_parms(self, parms, CONF_MODELS): | |
266 |
|
268 | |||
267 | experiments = Experiment.objects.filter(campaign = self) |
|
269 | experiments = Experiment.objects.filter(campaign = self) | |
268 |
|
270 | |||
269 | if experiments: |
|
271 | if experiments: | |
270 | for experiment in experiments: |
|
272 | for experiment in experiments: | |
271 | experiment.delete() |
|
273 | experiment.delete() | |
272 |
|
274 | |||
273 | for id_exp in parms['experiments']['allIds']: |
|
275 | for id_exp in parms['experiments']['allIds']: | |
274 | exp_parms = parms['experiments']['byId'][id_exp] |
|
276 | exp_parms = parms['experiments']['byId'][id_exp] | |
275 | dum = (datetime.now() - datetime(1970, 1, 1)).total_seconds() |
|
277 | dum = (datetime.now() - datetime(1970, 1, 1)).total_seconds() | |
276 | exp = Experiment(name='{}'.format(dum)) |
|
278 | exp = Experiment(name='{}'.format(dum)) | |
277 | exp.save() |
|
279 | exp.save() | |
278 | exp.dict_to_parms(parms, CONF_MODELS, id_exp=id_exp) |
|
280 | exp.dict_to_parms(parms, CONF_MODELS, id_exp=id_exp) | |
279 | self.experiments.add(exp) |
|
281 | self.experiments.add(exp) | |
280 |
|
282 | |||
281 | camp_parms = parms['campaigns']['byId'][parms['campaigns']['allIds'][0]] |
|
283 | camp_parms = parms['campaigns']['byId'][parms['campaigns']['allIds'][0]] | |
282 |
|
284 | |||
283 | self.name = '{}-{}'.format(camp_parms['name'], datetime.now().strftime('%y%m%d')) |
|
285 | self.name = '{}-{}'.format(camp_parms['name'], datetime.now().strftime('%y%m%d')) | |
284 | self.start_date = camp_parms['start_date'] |
|
286 | self.start_date = camp_parms['start_date'] | |
285 | self.end_date = camp_parms['end_date'] |
|
287 | self.end_date = camp_parms['end_date'] | |
286 | self.tags = camp_parms['tags'] |
|
288 | self.tags = camp_parms['tags'] | |
287 | self.save() |
|
289 | self.save() | |
288 |
|
290 | |||
289 | return self |
|
291 | return self | |
290 |
|
292 | |||
291 | def get_experiments_by_radar(self, radar=None): |
|
293 | def get_experiments_by_radar(self, radar=None): | |
292 |
|
294 | |||
293 | ret = [] |
|
295 | ret = [] | |
294 | if radar: |
|
296 | if radar: | |
295 | locations = Location.objects.filter(pk=radar) |
|
297 | locations = Location.objects.filter(pk=radar) | |
296 | else: |
|
298 | else: | |
297 | locations = set([e.location for e in self.experiments.all()]) |
|
299 | locations = set([e.location for e in self.experiments.all()]) | |
298 |
|
300 | |||
299 | for loc in locations: |
|
301 | for loc in locations: | |
300 | dum = {} |
|
302 | dum = {} | |
301 | dum['name'] = loc.name |
|
303 | dum['name'] = loc.name | |
302 | dum['id'] = loc.pk |
|
304 | dum['id'] = loc.pk | |
303 | dum['experiments'] = [e for e in self.experiments.all() if e.location==loc] |
|
305 | dum['experiments'] = [e for e in self.experiments.all() if e.location==loc] | |
304 | ret.append(dum) |
|
306 | ret.append(dum) | |
305 |
|
307 | |||
306 | return ret |
|
308 | return ret | |
307 |
|
309 | |||
308 | def get_absolute_url(self): |
|
310 | def get_absolute_url(self): | |
309 | return reverse('url_campaign', args=[str(self.id)]) |
|
311 | return reverse('url_campaign', args=[str(self.id)]) | |
310 |
|
312 | |||
311 | def get_absolute_url_edit(self): |
|
313 | def get_absolute_url_edit(self): | |
312 | return reverse('url_edit_campaign', args=[str(self.id)]) |
|
314 | return reverse('url_edit_campaign', args=[str(self.id)]) | |
313 |
|
315 | |||
314 | def get_absolute_url_delete(self): |
|
316 | def get_absolute_url_delete(self): | |
315 | return reverse('url_delete_campaign', args=[str(self.id)]) |
|
317 | return reverse('url_delete_campaign', args=[str(self.id)]) | |
316 |
|
318 | |||
317 | def get_absolute_url_export(self): |
|
319 | def get_absolute_url_export(self): | |
318 | return reverse('url_export_campaign', args=[str(self.id)]) |
|
320 | return reverse('url_export_campaign', args=[str(self.id)]) | |
319 |
|
321 | |||
320 | def get_absolute_url_import(self): |
|
322 | def get_absolute_url_import(self): | |
321 | return reverse('url_import_campaign', args=[str(self.id)]) |
|
323 | return reverse('url_import_campaign', args=[str(self.id)]) | |
322 |
|
324 | |||
323 |
|
325 | |||
324 | class RunningExperiment(models.Model): |
|
326 | class RunningExperiment(models.Model): | |
325 | radar = models.OneToOneField('Location', on_delete=models.CASCADE) |
|
327 | radar = models.OneToOneField('Location', on_delete=models.CASCADE) | |
326 | running_experiment = models.ManyToManyField('Experiment', blank = True) |
|
328 | running_experiment = models.ManyToManyField('Experiment', blank = True) | |
327 | status = models.PositiveSmallIntegerField(default=0, choices=RADAR_STATES) |
|
329 | status = models.PositiveSmallIntegerField(default=0, choices=RADAR_STATES) | |
328 |
|
330 | |||
329 |
|
331 | |||
330 | class Experiment(models.Model): |
|
332 | class Experiment(models.Model): | |
331 |
|
333 | |||
332 | template = models.BooleanField(default=False) |
|
334 | template = models.BooleanField(default=False) | |
333 | name = models.CharField(max_length=40, default='', unique=True) |
|
335 | name = models.CharField(max_length=40, default='', unique=True) | |
334 | location = models.ForeignKey('Location', null=False, blank=False, on_delete=models.CASCADE, default='') |
|
336 | location = models.ForeignKey('Location', null=False, blank=False, on_delete=models.CASCADE, default='') | |
335 | #freq = models.FloatField(verbose_name='Operating Freq. (MHz)', validators=[MinValueValidator(1), MaxValueValidator(10000)], default=49.9200) |
|
337 | #freq = models.FloatField(verbose_name='Operating Freq. (MHz)', validators=[MinValueValidator(1), MaxValueValidator(10000)], default=49.9200) | |
336 | start_time = models.TimeField(default='00:00:00') |
|
338 | start_time = models.TimeField(default='00:00:00') | |
337 | end_time = models.TimeField(default='23:59:59') |
|
339 | end_time = models.TimeField(default='23:59:59') | |
338 | task = models.CharField(max_length=36, default='', blank=True, null=True) |
|
340 | task = models.CharField(max_length=36, default='', blank=True, null=True) | |
339 | status = models.PositiveSmallIntegerField(default=4, choices=EXP_STATES) |
|
341 | status = models.PositiveSmallIntegerField(default=4, choices=EXP_STATES) | |
340 | author = models.ForeignKey(User, null=True, blank=True,on_delete=models.CASCADE) |
|
342 | author = models.ForeignKey(User, null=True, blank=True,on_delete=models.CASCADE) | |
341 | hash = models.CharField(default='', max_length=64, null=True, blank=True) |
|
343 | hash = models.CharField(default='', max_length=64, null=True, blank=True) | |
342 |
|
344 | |||
343 | class Meta: |
|
345 | class Meta: | |
344 | db_table = 'db_experiments' |
|
346 | db_table = 'db_experiments' | |
345 | ordering = ('template', 'name') |
|
347 | ordering = ('template', 'name') | |
346 |
|
348 | |||
347 | def __str__(self): |
|
349 | def __str__(self): | |
348 | if self.template: |
|
350 | if self.template: | |
349 | return u'%s (template)' % (self.name) |
|
351 | return u'%s (template)' % (self.name) | |
350 | else: |
|
352 | else: | |
351 | return u'%s' % (self.name) |
|
353 | return u'%s' % (self.name) | |
352 |
|
354 | |||
353 | def jsonify(self): |
|
355 | def jsonify(self): | |
354 |
|
356 | |||
355 | data = {} |
|
357 | data = {} | |
356 |
|
358 | |||
357 | ignored = ('template') |
|
359 | ignored = ('template') | |
358 |
|
360 | |||
359 | for field in self._meta.fields: |
|
361 | for field in self._meta.fields: | |
360 | if field.name in ignored: |
|
362 | if field.name in ignored: | |
361 | continue |
|
363 | continue | |
362 | data[field.name] = field.value_from_object(self) |
|
364 | data[field.name] = field.value_from_object(self) | |
363 |
|
365 | |||
364 | data['start_time'] = data['start_time'].strftime('%H:%M:%S') |
|
366 | data['start_time'] = data['start_time'].strftime('%H:%M:%S') | |
365 | data['end_time'] = data['end_time'].strftime('%H:%M:%S') |
|
367 | data['end_time'] = data['end_time'].strftime('%H:%M:%S') | |
366 | data['location'] = self.location.name |
|
368 | data['location'] = self.location.name | |
367 | data['configurations'] = ['{}'.format(conf.pk) for |
|
369 | data['configurations'] = ['{}'.format(conf.pk) for | |
368 | conf in Configuration.objects.filter(experiment=self, type=0)] |
|
370 | conf in Configuration.objects.filter(experiment=self, type=0)] | |
369 |
|
371 | |||
370 | return data |
|
372 | return data | |
371 |
|
373 | |||
372 | @property |
|
374 | @property | |
373 | def radar_system(self): |
|
375 | def radar_system(self): | |
374 | return self.location |
|
376 | return self.location | |
375 |
|
377 | |||
376 | def clone(self, **kwargs): |
|
378 | def clone(self, **kwargs): | |
377 |
|
379 | |||
378 | confs = Configuration.objects.filter(experiment=self, type=0) |
|
380 | confs = Configuration.objects.filter(experiment=self, type=0) | |
379 | self.pk = None |
|
381 | self.pk = None | |
380 | self.name = '{}_{:%y%m%d}'.format(self.name, datetime.now()) |
|
382 | self.name = '{}_{:%y%m%d}'.format(self.name, datetime.now()) | |
381 | for attr, value in kwargs.items(): |
|
383 | for attr, value in kwargs.items(): | |
382 | setattr(self, attr, value) |
|
384 | setattr(self, attr, value) | |
383 |
|
385 | |||
384 | self.save() |
|
386 | self.save() | |
385 |
|
387 | |||
386 | for conf in confs: |
|
388 | for conf in confs: | |
387 | conf.clone(experiment=self, template=False) |
|
389 | conf.clone(experiment=self, template=False) | |
388 |
|
390 | |||
389 | return self |
|
391 | return self | |
390 |
|
392 | |||
391 | def start(self): |
|
393 | def start(self): | |
392 | ''' |
|
394 | ''' | |
393 | Configure and start experiments's devices |
|
395 | Configure and start experiments's devices | |
394 | ''' |
|
396 | ''' | |
395 |
|
397 | |||
396 | confs = [] |
|
398 | confs = [] | |
397 | allconfs = Configuration.objects.filter(experiment=self, type = 0).order_by('-device__device_type__sequence') |
|
399 | allconfs = Configuration.objects.filter(experiment=self, type = 0).order_by('-device__device_type__sequence') | |
398 | confs = allconfs |
|
400 | confs = allconfs | |
399 |
|
401 | |||
400 | try: |
|
402 | try: | |
401 | for conf in confs: |
|
403 | for conf in confs: | |
402 | conf.stop_device() |
|
404 | conf.stop_device() | |
403 | print("OK") |
|
405 | print("OK") | |
404 | #conf.write_device() |
|
406 | #conf.write_device() | |
405 | conf.device.conf_active = conf.pk |
|
407 | conf.device.conf_active = conf.pk | |
406 | conf.device.save() |
|
408 | conf.device.save() | |
407 | conf.start_device() |
|
409 | conf.start_device() | |
408 | print("OK") |
|
410 | print("OK") | |
409 | time.sleep(1) |
|
411 | time.sleep(1) | |
410 | except: |
|
412 | except: | |
411 | return 0 |
|
413 | return 0 | |
412 | return 2 |
|
414 | return 2 | |
413 |
|
415 | |||
414 |
|
416 | |||
415 | def stop(self): |
|
417 | def stop(self): | |
416 | ''' |
|
418 | ''' | |
417 | Stop experiments's devices |
|
419 | Stop experiments's devices | |
418 | PEDESTAL, PULSE GENERATOR & USRP's |
|
420 | PEDESTAL, PULSE GENERATOR & USRP's | |
419 | ''' |
|
421 | ''' | |
420 |
|
422 | |||
421 | confs = Configuration.objects.filter(experiment=self, type = 0).order_by('device__device_type__sequence') |
|
423 | confs = Configuration.objects.filter(experiment=self, type = 0).order_by('device__device_type__sequence') | |
422 | try: |
|
424 | try: | |
423 | for conf in confs: |
|
425 | for conf in confs: | |
424 | conf.stop_device() |
|
426 | conf.stop_device() | |
425 | except: |
|
427 | except: | |
426 | return 0 |
|
428 | return 0 | |
427 | return 1 |
|
429 | return 1 | |
428 |
|
430 | |||
429 | def get_status(self): |
|
431 | def get_status(self): | |
430 |
|
432 | |||
431 | if self.status == 3: |
|
433 | if self.status == 3: | |
432 | return |
|
434 | return | |
433 |
|
435 | |||
434 | confs = Configuration.objects.filter(experiment=self, type=0) |
|
436 | confs = Configuration.objects.filter(experiment=self, type=0) | |
435 |
|
437 | |||
436 | for conf in confs: |
|
438 | for conf in confs: | |
437 | conf.status_device() |
|
439 | conf.status_device() | |
438 |
|
440 | |||
439 | total = confs.aggregate(models.Sum('device__status'))['device__status__sum'] |
|
441 | total = confs.aggregate(models.Sum('device__status'))['device__status__sum'] | |
440 |
|
442 | |||
441 | if total==2*confs.count(): |
|
443 | if total==2*confs.count(): | |
442 | status = 1 |
|
444 | status = 1 | |
443 | elif total == 3*confs.count(): |
|
445 | elif total == 3*confs.count(): | |
444 | status = 2 |
|
446 | status = 2 | |
445 | else: |
|
447 | else: | |
446 | status = 0 |
|
448 | status = 0 | |
447 |
|
449 | |||
448 | self.status = status |
|
450 | self.status = status | |
449 | self.save() |
|
451 | self.save() | |
450 |
|
452 | |||
451 | def status_color(self): |
|
453 | def status_color(self): | |
452 | color = 'muted' |
|
454 | color = 'muted' | |
453 | if self.status == 0: |
|
455 | if self.status == 0: | |
454 | color = "danger" |
|
456 | color = "danger" | |
455 | elif self.status == 1: |
|
457 | elif self.status == 1: | |
456 | color = "warning" |
|
458 | color = "warning" | |
457 | elif self.status == 2: |
|
459 | elif self.status == 2: | |
458 | color = "success" |
|
460 | color = "success" | |
459 | elif self.status == 3: |
|
461 | elif self.status == 3: | |
460 | color = "info" |
|
462 | color = "info" | |
461 |
|
463 | |||
462 | return color |
|
464 | return color | |
463 |
|
465 | |||
464 | def parms_to_dict(self): |
|
466 | def parms_to_dict(self): | |
465 |
|
467 | |||
466 | params = Params({}) |
|
468 | params = Params({}) | |
467 | params.add(self.jsonify(), 'experiments') |
|
469 | params.add(self.jsonify(), 'experiments') | |
468 |
|
470 | |||
469 | configurations = Configuration.objects.filter(experiment=self, type=0) |
|
471 | configurations = Configuration.objects.filter(experiment=self, type=0) | |
470 |
|
472 | |||
471 | for conf in configurations: |
|
473 | for conf in configurations: | |
472 | params.add(conf.jsonify(), 'configurations') |
|
474 | params.add(conf.jsonify(), 'configurations') | |
473 |
|
475 | |||
474 | return params.data |
|
476 | return params.data | |
475 |
|
477 | |||
476 | def dict_to_parms(self, parms, CONF_MODELS, id_exp=None): |
|
478 | def dict_to_parms(self, parms, CONF_MODELS, id_exp=None): | |
477 |
|
479 | |||
478 | configurations = Configuration.objects.filter(experiment=self) |
|
480 | configurations = Configuration.objects.filter(experiment=self) | |
479 |
|
481 | |||
480 | if id_exp is not None: |
|
482 | if id_exp is not None: | |
481 | exp_parms = parms['experiments']['byId'][id_exp] |
|
483 | exp_parms = parms['experiments']['byId'][id_exp] | |
482 | else: |
|
484 | else: | |
483 | exp_parms = parms['experiments']['byId'][parms['experiments']['allIds'][0]] |
|
485 | exp_parms = parms['experiments']['byId'][parms['experiments']['allIds'][0]] | |
484 |
|
486 | |||
485 | if configurations: |
|
487 | if configurations: | |
486 | for configuration in configurations: |
|
488 | for configuration in configurations: | |
487 | configuration.delete() |
|
489 | configuration.delete() | |
488 |
|
490 | |||
489 | for id_conf in exp_parms['configurations']: |
|
491 | for id_conf in exp_parms['configurations']: | |
490 | conf_parms = parms['configurations']['byId'][id_conf] |
|
492 | conf_parms = parms['configurations']['byId'][id_conf] | |
491 | device = Device.objects.filter(device_type__name=conf_parms['device_type'])[0] |
|
493 | device = Device.objects.filter(device_type__name=conf_parms['device_type'])[0] | |
492 | model = CONF_MODELS[conf_parms['device_type']] |
|
494 | model = CONF_MODELS[conf_parms['device_type']] | |
493 | conf = model( |
|
495 | conf = model( | |
494 | experiment = self, |
|
496 | experiment = self, | |
495 | device = device, |
|
497 | device = device, | |
496 | ) |
|
498 | ) | |
497 | conf.dict_to_parms(parms, id=id_conf) |
|
499 | conf.dict_to_parms(parms, id=id_conf) | |
498 |
|
500 | |||
499 |
|
501 | |||
500 | location, created = Location.objects.get_or_create(name=exp_parms['location']) |
|
502 | location, created = Location.objects.get_or_create(name=exp_parms['location']) | |
501 | self.name = '{}-{}'.format(exp_parms['name'], datetime.now().strftime('%y%m%d')) |
|
503 | self.name = '{}-{}'.format(exp_parms['name'], datetime.now().strftime('%y%m%d')) | |
502 | self.location = location |
|
504 | self.location = location | |
503 | self.start_time = exp_parms['start_time'] |
|
505 | self.start_time = exp_parms['start_time'] | |
504 | self.end_time = exp_parms['end_time'] |
|
506 | self.end_time = exp_parms['end_time'] | |
505 | self.save() |
|
507 | self.save() | |
506 |
|
508 | |||
507 | return self |
|
509 | return self | |
508 |
|
510 | |||
509 | def get_absolute_url(self): |
|
511 | def get_absolute_url(self): | |
510 | return reverse('url_experiment', args=[str(self.id)]) |
|
512 | return reverse('url_experiment', args=[str(self.id)]) | |
511 |
|
513 | |||
512 | def get_absolute_url_edit(self): |
|
514 | def get_absolute_url_edit(self): | |
513 | return reverse('url_edit_experiment', args=[str(self.id)]) |
|
515 | return reverse('url_edit_experiment', args=[str(self.id)]) | |
514 |
|
516 | |||
515 | def get_absolute_url_delete(self): |
|
517 | def get_absolute_url_delete(self): | |
516 | return reverse('url_delete_experiment', args=[str(self.id)]) |
|
518 | return reverse('url_delete_experiment', args=[str(self.id)]) | |
517 |
|
519 | |||
518 | def get_absolute_url_import(self): |
|
520 | def get_absolute_url_import(self): | |
519 | return reverse('url_import_experiment', args=[str(self.id)]) |
|
521 | return reverse('url_import_experiment', args=[str(self.id)]) | |
520 |
|
522 | |||
521 | def get_absolute_url_export(self): |
|
523 | def get_absolute_url_export(self): | |
522 | return reverse('url_export_experiment', args=[str(self.id)]) |
|
524 | return reverse('url_export_experiment', args=[str(self.id)]) | |
523 |
|
525 | |||
524 | def get_absolute_url_start(self): |
|
526 | def get_absolute_url_start(self): | |
525 | return reverse('url_start_experiment', args=[str(self.id)]) |
|
527 | return reverse('url_start_experiment', args=[str(self.id)]) | |
526 |
|
528 | |||
527 | def get_absolute_url_stop(self): |
|
529 | def get_absolute_url_stop(self): | |
528 | return reverse('url_stop_experiment', args=[str(self.id)]) |
|
530 | return reverse('url_stop_experiment', args=[str(self.id)]) | |
529 |
|
531 | |||
530 |
|
532 | |||
531 | class Configuration(PolymorphicModel): |
|
533 | class Configuration(PolymorphicModel): | |
532 |
|
534 | |||
533 | id = models.AutoField(primary_key=True) |
|
535 | id = models.AutoField(primary_key=True) | |
534 | template = models.BooleanField(default=False) |
|
536 | template = models.BooleanField(default=False) | |
535 | # name = models.CharField(verbose_name="Configuration Name", max_length=40, default='') |
|
537 | # name = models.CharField(verbose_name="Configuration Name", max_length=40, default='') | |
536 | device = models.ForeignKey('Device', verbose_name='Device', null=True, on_delete=models.CASCADE) |
|
538 | device = models.ForeignKey('Device', verbose_name='Device', null=True, on_delete=models.CASCADE) | |
537 | label = models.CharField(verbose_name="Label", max_length=40, default='', blank=True, null=True) |
|
539 | label = models.CharField(verbose_name="Label", max_length=40, default='', blank=True, null=True) | |
538 | experiment = models.ForeignKey('Experiment', verbose_name='Experiment', null=True, blank=True, on_delete=models.CASCADE) |
|
540 | experiment = models.ForeignKey('Experiment', verbose_name='Experiment', null=True, blank=True, on_delete=models.CASCADE) | |
539 | type = models.PositiveSmallIntegerField(default=0, choices=CONF_TYPES) |
|
541 | type = models.PositiveSmallIntegerField(default=0, choices=CONF_TYPES) | |
540 | created_date = models.DateTimeField(auto_now_add=True) |
|
542 | created_date = models.DateTimeField(auto_now_add=True) | |
541 | programmed_date = models.DateTimeField(auto_now=True) |
|
543 | programmed_date = models.DateTimeField(auto_now=True) | |
542 | parameters = models.TextField(default='{}') |
|
544 | parameters = models.TextField(default='{}') | |
543 | author = models.ForeignKey(User, null=True, blank=True,on_delete=models.CASCADE) |
|
545 | author = models.ForeignKey(User, null=True, blank=True,on_delete=models.CASCADE) | |
544 | hash = models.CharField(default='', max_length=64, null=True, blank=True) |
|
546 | hash = models.CharField(default='', max_length=64, null=True, blank=True) | |
545 | message = "" |
|
547 | message = "" | |
546 |
|
548 | |||
547 | class Meta: |
|
549 | class Meta: | |
548 | db_table = 'db_configurations' |
|
550 | db_table = 'db_configurations' | |
549 | ordering = ('device__device_type__name',) |
|
551 | ordering = ('device__device_type__name',) | |
550 |
|
552 | |||
551 | def __str__(self): |
|
553 | def __str__(self): | |
552 |
|
554 | |||
553 | ret = u'{} '.format(self.device.device_type.name.upper()) |
|
555 | ret = u'{} '.format(self.device.device_type.name.upper()) | |
554 |
|
556 | |||
555 | if 'mix' in [f.name for f in self._meta.get_fields()]: |
|
557 | if 'mix' in [f.name for f in self._meta.get_fields()]: | |
556 | if self.mix: |
|
558 | if self.mix: | |
557 | ret = '{} MIX '.format(self.device.device_type.name.upper()) |
|
559 | ret = '{} MIX '.format(self.device.device_type.name.upper()) | |
558 |
|
560 | |||
559 | if 'label' in [f.name for f in self._meta.get_fields()]: |
|
561 | if 'label' in [f.name for f in self._meta.get_fields()]: | |
560 | ret += '{}'.format(self.label) |
|
562 | ret += '{}'.format(self.label) | |
561 |
|
563 | |||
562 | if self.template: |
|
564 | if self.template: | |
563 | ret += ' (template)' |
|
565 | ret += ' (template)' | |
564 |
|
566 | |||
565 | return ret |
|
567 | return ret | |
566 |
|
568 | |||
567 | @property |
|
569 | @property | |
568 | def name(self): |
|
570 | def name(self): | |
569 |
|
571 | |||
570 | return str(self) |
|
572 | return str(self) | |
571 |
|
573 | |||
572 | def jsonify(self): |
|
574 | def jsonify(self): | |
573 |
|
575 | |||
574 | data = {} |
|
576 | data = {} | |
575 |
|
577 | |||
576 | ignored = ('type', 'polymorphic_ctype', 'configuration_ptr', |
|
578 | ignored = ('type', 'polymorphic_ctype', 'configuration_ptr', | |
577 | 'created_date', 'programmed_date', 'template', 'device', |
|
579 | 'created_date', 'programmed_date', 'template', 'device', | |
578 | 'experiment') |
|
580 | 'experiment') | |
579 |
|
581 | |||
580 | for field in self._meta.fields: |
|
582 | for field in self._meta.fields: | |
581 | if field.name in ignored: |
|
583 | if field.name in ignored: | |
582 | continue |
|
584 | continue | |
583 | data[field.name] = field.value_from_object(self) |
|
585 | data[field.name] = field.value_from_object(self) | |
584 |
|
586 | |||
585 | data['device_type'] = self.device.device_type.name |
|
587 | data['device_type'] = self.device.device_type.name | |
586 | return data |
|
588 | return data | |
587 |
|
589 | |||
588 | def clone(self, **kwargs): |
|
590 | def clone(self, **kwargs): | |
589 |
|
591 | |||
590 | self.pk = None |
|
592 | self.pk = None | |
591 | self.id = None |
|
593 | self.id = None | |
592 | for attr, value in kwargs.items(): |
|
594 | for attr, value in kwargs.items(): | |
593 | setattr(self, attr, value) |
|
595 | setattr(self, attr, value) | |
594 |
|
596 | |||
595 | self.save() |
|
597 | self.save() | |
596 |
|
598 | |||
597 | return self |
|
599 | return self | |
598 |
|
600 | |||
599 | def parms_to_dict(self): |
|
601 | def parms_to_dict(self): | |
600 |
|
602 | |||
601 | params = Params({}) |
|
603 | params = Params({}) | |
602 | params.add(self.jsonify(), 'configurations') |
|
604 | params.add(self.jsonify(), 'configurations') | |
603 | return params.data |
|
605 | return params.data | |
604 |
|
606 | |||
605 | def parms_to_text(self): |
|
607 | def parms_to_text(self): | |
606 |
|
608 | |||
607 | raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) |
|
609 | raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) | |
608 |
|
610 | |||
609 |
|
611 | |||
610 | def parms_to_binary(self): |
|
612 | def parms_to_binary(self): | |
611 |
|
613 | |||
612 | raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) |
|
614 | raise NotImplementedError("This method should be implemented in %s Configuration model" %str(self.device.device_type.name).upper()) | |
613 |
|
615 | |||
614 |
|
616 | |||
615 | def dict_to_parms(self, parameters, id=None): |
|
617 | def dict_to_parms(self, parameters, id=None): | |
616 |
|
618 | |||
617 | params = Params(parameters) |
|
619 | params = Params(parameters) | |
618 |
|
620 | |||
619 | if id: |
|
621 | if id: | |
620 | data = params.get_conf(id_conf=id) |
|
622 | data = params.get_conf(id_conf=id) | |
621 | else: |
|
623 | else: | |
622 | data = params.get_conf(dtype=self.device.device_type.name) |
|
624 | data = params.get_conf(dtype=self.device.device_type.name) | |
623 |
|
625 | |||
624 | for key, value in data.items(): |
|
626 | for key, value in data.items(): | |
625 | if key not in ('id', 'device_type'): |
|
627 | if key not in ('id', 'device_type'): | |
626 | setattr(self, key, value) |
|
628 | setattr(self, key, value) | |
627 |
|
629 | |||
628 | self.save() |
|
630 | self.save() | |
629 |
|
631 | |||
630 |
|
632 | |||
631 | def export_to_file(self, format="json"): |
|
633 | def export_to_file(self, format="json"): | |
632 |
|
634 | |||
633 | content_type = '' |
|
635 | content_type = '' | |
634 |
|
636 | |||
635 | if format == 'racp': |
|
637 | if format == 'racp': | |
636 | content_type = 'text/plain' |
|
638 | content_type = 'text/plain' | |
637 | filename = '%s_%s.%s' %(self.device.device_type.name, self.name, 'racp') |
|
639 | filename = '%s_%s.%s' %(self.device.device_type.name, self.name, 'racp') | |
638 | content = self.parms_to_text(file_format = 'racp') |
|
640 | content = self.parms_to_text(file_format = 'racp') | |
639 |
|
641 | |||
640 | if format == 'text': |
|
642 | if format == 'text': | |
641 | content_type = 'text/plain' |
|
643 | content_type = 'text/plain' | |
642 | filename = '%s_%s.%s' %(self.device.device_type.name, self.name, self.device.device_type.name) |
|
644 | filename = '%s_%s.%s' %(self.device.device_type.name, self.name, self.device.device_type.name) | |
643 | content = self.parms_to_text() |
|
645 | content = self.parms_to_text() | |
644 |
|
646 | |||
645 | if format == 'binary': |
|
647 | if format == 'binary': | |
646 | content_type = 'application/octet-stream' |
|
648 | content_type = 'application/octet-stream' | |
647 | filename = '%s_%s.bin' %(self.device.device_type.name, self.name) |
|
649 | filename = '%s_%s.bin' %(self.device.device_type.name, self.name) | |
648 | content = self.parms_to_binary() |
|
650 | content = self.parms_to_binary() | |
649 |
|
651 | |||
650 | if not content_type: |
|
652 | if not content_type: | |
651 | content_type = 'application/json' |
|
653 | content_type = 'application/json' | |
652 | filename = '%s_%s.json' %(self.device.device_type.name, self.name) |
|
654 | filename = '%s_%s.json' %(self.device.device_type.name, self.name) | |
653 | content = json.dumps(self.parms_to_dict(), indent=2) |
|
655 | content = json.dumps(self.parms_to_dict(), indent=2) | |
654 |
|
656 | |||
655 | fields = {'content_type':content_type, |
|
657 | fields = {'content_type':content_type, | |
656 | 'filename':filename, |
|
658 | 'filename':filename, | |
657 | 'content':content |
|
659 | 'content':content | |
658 | } |
|
660 | } | |
659 |
|
661 | |||
660 | return fields |
|
662 | return fields | |
661 |
|
663 | |||
662 | def import_from_file(self, fp): |
|
664 | def import_from_file(self, fp): | |
663 |
|
665 | |||
664 | parms = {} |
|
666 | parms = {} | |
665 |
|
667 | |||
666 | path, ext = os.path.splitext(fp.name) |
|
668 | path, ext = os.path.splitext(fp.name) | |
667 |
|
669 | |||
668 | if ext == '.json': |
|
670 | if ext == '.json': | |
669 | parms = json.load(fp) |
|
671 | parms = json.load(fp) | |
670 |
|
672 | |||
671 | return parms |
|
673 | return parms | |
672 |
|
674 | |||
673 | def status_device(self): |
|
675 | def status_device(self): | |
674 |
|
676 | |||
675 | self.message = 'Function not implemented' |
|
677 | self.message = 'Function not implemented' | |
676 | return False |
|
678 | return False | |
677 |
|
679 | |||
678 |
|
680 | |||
679 | def stop_device(self): |
|
681 | def stop_device(self): | |
680 |
|
682 | |||
681 | self.message = 'Function not implemented' |
|
683 | self.message = 'Function not implemented' | |
682 | return False |
|
684 | return False | |
683 |
|
685 | |||
684 |
|
686 | |||
685 | def start_device(self): |
|
687 | def start_device(self): | |
686 |
|
688 | |||
687 | self.message = 'Function not implemented' |
|
689 | self.message = 'Function not implemented' | |
688 | return False |
|
690 | return False | |
689 |
|
691 | |||
690 |
|
692 | |||
691 | def write_device(self): |
|
693 | def write_device(self): | |
692 |
|
694 | |||
693 | self.message = 'Function not implemented' |
|
695 | self.message = 'Function not implemented' | |
694 | return False |
|
696 | return False | |
695 |
|
697 | |||
696 |
|
698 | |||
697 | def read_device(self): |
|
699 | def read_device(self): | |
698 |
|
700 | |||
699 | self.message = 'Function not implemented' |
|
701 | self.message = 'Function not implemented' | |
700 | return False |
|
702 | return False | |
701 |
|
703 | |||
702 |
|
704 | |||
703 | def get_absolute_url(self): |
|
705 | def get_absolute_url(self): | |
704 | return reverse('url_%s_conf' % self.device.device_type.name, args=[str(self.id)]) |
|
706 | return reverse('url_%s_conf' % self.device.device_type.name, args=[str(self.id)]) | |
705 |
|
707 | |||
706 | def get_absolute_url_edit(self): |
|
708 | def get_absolute_url_edit(self): | |
707 | return reverse('url_edit_%s_conf' % self.device.device_type.name, args=[str(self.id)]) |
|
709 | return reverse('url_edit_%s_conf' % self.device.device_type.name, args=[str(self.id)]) | |
708 |
|
710 | |||
709 | def get_absolute_url_delete(self): |
|
711 | def get_absolute_url_delete(self): | |
710 | return reverse('url_delete_dev_conf', args=[str(self.id)]) |
|
712 | return reverse('url_delete_dev_conf', args=[str(self.id)]) | |
711 |
|
713 | |||
712 | def get_absolute_url_import(self): |
|
714 | def get_absolute_url_import(self): | |
713 | return reverse('url_import_dev_conf', args=[str(self.id)]) |
|
715 | return reverse('url_import_dev_conf', args=[str(self.id)]) | |
714 |
|
716 | |||
715 | def get_absolute_url_export(self): |
|
717 | def get_absolute_url_export(self): | |
716 | return reverse('url_export_dev_conf', args=[str(self.id)]) |
|
718 | return reverse('url_export_dev_conf', args=[str(self.id)]) | |
717 |
|
719 | |||
718 | def get_absolute_url_write(self): |
|
720 | def get_absolute_url_write(self): | |
719 | return reverse('url_write_dev_conf', args=[str(self.id)]) |
|
721 | return reverse('url_write_dev_conf', args=[str(self.id)]) | |
720 |
|
722 | |||
721 | def get_absolute_url_read(self): |
|
723 | def get_absolute_url_read(self): | |
722 | return reverse('url_read_dev_conf', args=[str(self.id)]) |
|
724 | return reverse('url_read_dev_conf', args=[str(self.id)]) | |
723 |
|
725 | |||
724 | def get_absolute_url_start(self): |
|
726 | def get_absolute_url_start(self): | |
725 | return reverse('url_start_dev_conf', args=[str(self.id)]) |
|
727 | return reverse('url_start_dev_conf', args=[str(self.id)]) | |
726 |
|
728 | |||
727 | def get_absolute_url_stop(self): |
|
729 | def get_absolute_url_stop(self): | |
728 | return reverse('url_stop_dev_conf', args=[str(self.id)]) |
|
730 | return reverse('url_stop_dev_conf', args=[str(self.id)]) | |
729 |
|
731 | |||
730 | def get_absolute_url_status(self): |
|
732 | def get_absolute_url_status(self): | |
731 | return reverse('url_status_dev_conf', args=[str(self.id)]) |
|
733 | return reverse('url_status_dev_conf', args=[str(self.id)]) |
@@ -1,1917 +1,1920 | |||||
1 | import ast |
|
1 | import ast | |
2 | import json |
|
2 | import json | |
3 | import hashlib |
|
3 | import hashlib | |
4 | from datetime import datetime, timedelta |
|
4 | from datetime import datetime, timedelta | |
5 |
|
5 | |||
6 | from django.shortcuts import render, redirect, get_object_or_404, HttpResponse |
|
6 | from django.shortcuts import render, redirect, get_object_or_404, HttpResponse | |
7 | from django.utils.safestring import mark_safe |
|
7 | from django.utils.safestring import mark_safe | |
8 | from django.http import HttpResponseRedirect |
|
8 | from django.http import HttpResponseRedirect | |
9 | from django.urls import reverse |
|
9 | from django.urls import reverse | |
10 | from django.db.models import Q |
|
10 | from django.db.models import Q | |
11 | from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger |
|
11 | from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger | |
12 | from django.contrib import messages |
|
12 | from django.contrib import messages | |
13 | from django.http.request import QueryDict |
|
13 | from django.http.request import QueryDict | |
14 | from django.contrib.auth.decorators import login_required, user_passes_test |
|
14 | from django.contrib.auth.decorators import login_required, user_passes_test | |
15 |
|
15 | |||
16 | from django.utils.timezone import is_aware |
|
16 | from django.utils.timezone import is_aware | |
17 |
|
17 | |||
18 | try: |
|
18 | try: | |
19 | from urllib.parse import urlencode |
|
19 | from urllib.parse import urlencode | |
20 | except ImportError: |
|
20 | except ImportError: | |
21 | from urllib import urlencode |
|
21 | from urllib import urlencode | |
22 |
|
22 | |||
23 | from .forms import CampaignForm, ExperimentForm, DeviceForm, ConfigurationForm, LocationForm, UploadFileForm, DownloadFileForm, OperationForm, NewForm |
|
23 | from .forms import CampaignForm, ExperimentForm, DeviceForm, ConfigurationForm, LocationForm, UploadFileForm, DownloadFileForm, OperationForm, NewForm | |
24 | from .forms import OperationSearchForm, FilterForm, ChangeIpForm |
|
24 | from .forms import OperationSearchForm, FilterForm, ChangeIpForm | |
25 |
|
25 | |||
26 | from apps.pedestal.forms import PedestalConfigurationForm |
|
26 | from apps.pedestal.forms import PedestalConfigurationForm | |
|
27 | from apps.pedestal_dev.forms import PedestalDevConfigurationForm | |||
27 | from apps.generator.forms import GeneratorConfigurationForm |
|
28 | from apps.generator.forms import GeneratorConfigurationForm | |
28 | from apps.usrp_rx.forms import USRPRXConfigurationForm |
|
29 | from apps.usrp_rx.forms import USRPRXConfigurationForm | |
29 | from apps.usrp_tx.forms import USRPTXConfigurationForm |
|
30 | from apps.usrp_tx.forms import USRPTXConfigurationForm | |
30 | from .utils import Params |
|
31 | from .utils import Params | |
31 |
|
32 | |||
32 | from .models import Campaign, Experiment, Device, Configuration, Location, RunningExperiment, DEV_STATES |
|
33 | from .models import Campaign, Experiment, Device, Configuration, Location, RunningExperiment, DEV_STATES | |
33 | from apps.pedestal.models import PedestalConfiguration |
|
34 | from apps.pedestal.models import PedestalConfiguration | |
|
35 | from apps.pedestal_dev.models import PedestalDevConfiguration | |||
34 | from apps.generator.models import GeneratorConfiguration |
|
36 | from apps.generator.models import GeneratorConfiguration | |
35 | from apps.usrp_rx.models import USRPRXConfiguration |
|
37 | from apps.usrp_rx.models import USRPRXConfiguration | |
36 | from apps.usrp_tx.models import USRPTXConfiguration |
|
38 | from apps.usrp_tx.models import USRPTXConfiguration | |
37 |
|
39 | |||
38 |
|
40 | |||
39 | #comentario test |
|
41 | #comentario test | |
40 | CONF_FORMS = { |
|
42 | CONF_FORMS = { | |
41 | 'pedestal': PedestalConfigurationForm, |
|
43 | 'pedestal': PedestalConfigurationForm, | |
|
44 | 'pedestal_dev': PedestalDevConfigurationForm, | |||
42 | 'generator': GeneratorConfigurationForm, |
|
45 | 'generator': GeneratorConfigurationForm, | |
43 | 'usrp_rx': USRPRXConfigurationForm, |
|
46 | 'usrp_rx': USRPRXConfigurationForm, | |
44 | 'usrp_tx': USRPTXConfigurationForm, |
|
47 | 'usrp_tx': USRPTXConfigurationForm, | |
45 | } |
|
48 | } | |
46 |
|
49 | |||
47 | CONF_MODELS = { |
|
50 | CONF_MODELS = { | |
48 | 'pedestal': PedestalConfiguration, |
|
51 | 'pedestal': PedestalConfiguration, | |
|
52 | 'pedestal_dev': PedestalDevConfiguration, | |||
49 | 'generator': GeneratorConfiguration, |
|
53 | 'generator': GeneratorConfiguration, | |
50 | 'usrp_rx': USRPRXConfiguration, |
|
54 | 'usrp_rx': USRPRXConfiguration, | |
51 | 'usrp_tx': USRPTXConfiguration, |
|
55 | 'usrp_tx': USRPTXConfiguration, | |
52 | } |
|
56 | } | |
53 |
|
57 | |||
54 | MIX_MODES = { |
|
58 | MIX_MODES = { | |
55 | '0': 'P', |
|
59 | '0': 'P', | |
56 | '1': 'S', |
|
60 | '1': 'S', | |
57 | } |
|
61 | } | |
58 |
|
62 | |||
59 | MIX_OPERATIONS = { |
|
63 | MIX_OPERATIONS = { | |
60 | '0': 'OR', |
|
64 | '0': 'OR', | |
61 | '1': 'XOR', |
|
65 | '1': 'XOR', | |
62 | '2': 'AND', |
|
66 | '2': 'AND', | |
63 | '3': 'NAND', |
|
67 | '3': 'NAND', | |
64 | } |
|
68 | } | |
65 |
|
69 | |||
66 |
|
70 | |||
67 | def is_developer(user): |
|
71 | def is_developer(user): | |
68 |
|
72 | |||
69 | groups = [str(g.name) for g in user.groups.all()] |
|
73 | groups = [str(g.name) for g in user.groups.all()] | |
70 | return 'Developer' in groups or user.is_staff |
|
74 | return 'Developer' in groups or user.is_staff | |
71 |
|
75 | |||
72 |
|
76 | |||
73 | def is_operator(user): |
|
77 | def is_operator(user): | |
74 |
|
78 | |||
75 | groups = [str(g.name) for g in user.groups.all()] |
|
79 | groups = [str(g.name) for g in user.groups.all()] | |
76 | return 'Operator' in groups or user.is_staff |
|
80 | return 'Operator' in groups or user.is_staff | |
77 |
|
81 | |||
78 |
|
82 | |||
79 | def has_been_modified(model): |
|
83 | def has_been_modified(model): | |
80 |
|
84 | |||
81 | prev_hash = model.hash |
|
85 | prev_hash = model.hash | |
82 | new_hash = hashlib.sha256(str(model.parms_to_dict).encode()).hexdigest() |
|
86 | new_hash = hashlib.sha256(str(model.parms_to_dict).encode()).hexdigest() | |
83 | if prev_hash != new_hash: |
|
87 | if prev_hash != new_hash: | |
84 | model.hash = new_hash |
|
88 | model.hash = new_hash | |
85 | model.save() |
|
89 | model.save() | |
86 | return True |
|
90 | return True | |
87 | return False |
|
91 | return False | |
88 |
|
92 | |||
89 |
|
93 | |||
90 | def index(request): |
|
94 | def index(request): | |
91 | kwargs = {'no_sidebar': True} |
|
95 | kwargs = {'no_sidebar': True} | |
92 |
|
96 | |||
93 | return render(request, 'index.html', kwargs) |
|
97 | return render(request, 'index.html', kwargs) | |
94 |
|
98 | |||
95 |
|
99 | |||
96 | def locations(request): |
|
100 | def locations(request): | |
97 |
|
101 | |||
98 | page = request.GET.get('page') |
|
102 | page = request.GET.get('page') | |
99 | order = ('name',) |
|
103 | order = ('name',) | |
100 |
|
104 | |||
101 | kwargs = get_paginator(Location, page, order) |
|
105 | kwargs = get_paginator(Location, page, order) | |
102 |
|
106 | |||
103 | kwargs['keys'] = ['name', 'description'] |
|
107 | kwargs['keys'] = ['name', 'description'] | |
104 | kwargs['title'] = 'Radar System' |
|
108 | kwargs['title'] = 'Radar System' | |
105 | kwargs['suptitle'] = 'List' |
|
109 | kwargs['suptitle'] = 'List' | |
106 | kwargs['no_sidebar'] = True |
|
110 | kwargs['no_sidebar'] = True | |
107 |
|
111 | |||
108 | return render(request, 'base_list.html', kwargs) |
|
112 | return render(request, 'base_list.html', kwargs) | |
109 |
|
113 | |||
110 |
|
114 | |||
111 | def location(request, id_loc): |
|
115 | def location(request, id_loc): | |
112 |
|
116 | |||
113 | location = get_object_or_404(Location, pk=id_loc) |
|
117 | location = get_object_or_404(Location, pk=id_loc) | |
114 |
|
118 | |||
115 | kwargs = {} |
|
119 | kwargs = {} | |
116 | kwargs['location'] = location |
|
120 | kwargs['location'] = location | |
117 | kwargs['location_keys'] = ['name', 'description'] |
|
121 | kwargs['location_keys'] = ['name', 'description'] | |
118 |
|
122 | |||
119 | kwargs['title'] = 'Location' |
|
123 | kwargs['title'] = 'Location' | |
120 | kwargs['suptitle'] = 'Details' |
|
124 | kwargs['suptitle'] = 'Details' | |
121 |
|
125 | |||
122 | return render(request, 'location.html', kwargs) |
|
126 | return render(request, 'location.html', kwargs) | |
123 |
|
127 | |||
124 |
|
128 | |||
125 | @login_required |
|
129 | @login_required | |
126 | def location_new(request): |
|
130 | def location_new(request): | |
127 |
|
131 | |||
128 | if request.method == 'GET': |
|
132 | if request.method == 'GET': | |
129 | form = LocationForm() |
|
133 | form = LocationForm() | |
130 |
|
134 | |||
131 | if request.method == 'POST': |
|
135 | if request.method == 'POST': | |
132 | form = LocationForm(request.POST) |
|
136 | form = LocationForm(request.POST) | |
133 |
|
137 | |||
134 | if form.is_valid(): |
|
138 | if form.is_valid(): | |
135 | form.save() |
|
139 | form.save() | |
136 | return redirect('url_locations') |
|
140 | return redirect('url_locations') | |
137 |
|
141 | |||
138 | kwargs = {} |
|
142 | kwargs = {} | |
139 | kwargs['form'] = form |
|
143 | kwargs['form'] = form | |
140 | kwargs['title'] = 'Radar System' |
|
144 | kwargs['title'] = 'Radar System' | |
141 | kwargs['suptitle'] = 'New' |
|
145 | kwargs['suptitle'] = 'New' | |
142 | kwargs['button'] = 'Create' |
|
146 | kwargs['button'] = 'Create' | |
143 |
|
147 | |||
144 | return render(request, 'base_edit.html', kwargs) |
|
148 | return render(request, 'base_edit.html', kwargs) | |
145 |
|
149 | |||
146 |
|
150 | |||
147 | @login_required |
|
151 | @login_required | |
148 | def location_edit(request, id_loc): |
|
152 | def location_edit(request, id_loc): | |
149 |
|
153 | |||
150 | location = get_object_or_404(Location, pk=id_loc) |
|
154 | location = get_object_or_404(Location, pk=id_loc) | |
151 |
|
155 | |||
152 | if request.method == 'GET': |
|
156 | if request.method == 'GET': | |
153 | form = LocationForm(instance=location) |
|
157 | form = LocationForm(instance=location) | |
154 |
|
158 | |||
155 | if request.method == 'POST': |
|
159 | if request.method == 'POST': | |
156 | form = LocationForm(request.POST, instance=location) |
|
160 | form = LocationForm(request.POST, instance=location) | |
157 |
|
161 | |||
158 | if form.is_valid(): |
|
162 | if form.is_valid(): | |
159 | form.save() |
|
163 | form.save() | |
160 | return redirect('url_locations') |
|
164 | return redirect('url_locations') | |
161 |
|
165 | |||
162 | kwargs = {} |
|
166 | kwargs = {} | |
163 | kwargs['form'] = form |
|
167 | kwargs['form'] = form | |
164 | kwargs['title'] = 'Location' |
|
168 | kwargs['title'] = 'Location' | |
165 | kwargs['suptitle'] = 'Edit' |
|
169 | kwargs['suptitle'] = 'Edit' | |
166 | kwargs['button'] = 'Update' |
|
170 | kwargs['button'] = 'Update' | |
167 |
|
171 | |||
168 | return render(request, 'base_edit.html', kwargs) |
|
172 | return render(request, 'base_edit.html', kwargs) | |
169 |
|
173 | |||
170 |
|
174 | |||
171 | @login_required |
|
175 | @login_required | |
172 | def location_delete(request, id_loc): |
|
176 | def location_delete(request, id_loc): | |
173 |
|
177 | |||
174 | location = get_object_or_404(Location, pk=id_loc) |
|
178 | location = get_object_or_404(Location, pk=id_loc) | |
175 |
|
179 | |||
176 | if request.method == 'POST': |
|
180 | if request.method == 'POST': | |
177 |
|
181 | |||
178 | if is_developer(request.user): |
|
182 | if is_developer(request.user): | |
179 | location.delete() |
|
183 | location.delete() | |
180 | return redirect('url_locations') |
|
184 | return redirect('url_locations') | |
181 |
|
185 | |||
182 | messages.error(request, 'Not enough permission to delete this object') |
|
186 | messages.error(request, 'Not enough permission to delete this object') | |
183 | return redirect(location.get_absolute_url()) |
|
187 | return redirect(location.get_absolute_url()) | |
184 |
|
188 | |||
185 | kwargs = { |
|
189 | kwargs = { | |
186 | 'title': 'Delete', |
|
190 | 'title': 'Delete', | |
187 | 'suptitle': 'Location', |
|
191 | 'suptitle': 'Location', | |
188 | 'object': location, |
|
192 | 'object': location, | |
189 | 'delete': True |
|
193 | 'delete': True | |
190 | } |
|
194 | } | |
191 |
|
195 | |||
192 | return render(request, 'confirm.html', kwargs) |
|
196 | return render(request, 'confirm.html', kwargs) | |
193 |
|
197 | |||
194 |
|
198 | |||
195 | def devices(request): |
|
199 | def devices(request): | |
196 |
|
200 | |||
197 | page = request.GET.get('page') |
|
201 | page = request.GET.get('page') | |
198 | order = ('location', 'device_type') |
|
202 | order = ('location', 'device_type') | |
199 |
|
203 | |||
200 | filters = request.GET.copy() |
|
204 | filters = request.GET.copy() | |
201 | kwargs = get_paginator(Device, page, order, filters) |
|
205 | kwargs = get_paginator(Device, page, order, filters) | |
202 | form = FilterForm(initial=request.GET, extra_fields=['tags']) |
|
206 | form = FilterForm(initial=request.GET, extra_fields=['tags']) | |
203 |
|
207 | |||
204 | kwargs['keys'] = ['device_type', 'location', |
|
208 | kwargs['keys'] = ['device_type', 'location', | |
205 | 'ip_address', 'port_address', 'actions'] |
|
209 | 'ip_address', 'port_address', 'actions'] | |
206 | kwargs['title'] = 'Device' |
|
210 | kwargs['title'] = 'Device' | |
207 | kwargs['suptitle'] = 'List' |
|
211 | kwargs['suptitle'] = 'List' | |
208 | kwargs['no_sidebar'] = True |
|
212 | kwargs['no_sidebar'] = True | |
209 | kwargs['form'] = form |
|
213 | kwargs['form'] = form | |
210 | kwargs['add_url'] = reverse('url_add_device') |
|
214 | kwargs['add_url'] = reverse('url_add_device') | |
211 | filters.pop('page', None) |
|
215 | filters.pop('page', None) | |
212 | kwargs['q'] = urlencode(filters) |
|
216 | kwargs['q'] = urlencode(filters) | |
213 | kwargs['menu_devices'] = 'active' |
|
217 | kwargs['menu_devices'] = 'active' | |
214 | return render(request, 'base_list.html', kwargs) |
|
218 | return render(request, 'base_list.html', kwargs) | |
215 |
|
219 | |||
216 |
|
220 | |||
217 | def device(request, id_dev): |
|
221 | def device(request, id_dev): | |
218 |
|
222 | |||
219 | device = get_object_or_404(Device, pk=id_dev) |
|
223 | device = get_object_or_404(Device, pk=id_dev) | |
220 |
|
224 | |||
221 | kwargs = {} |
|
225 | kwargs = {} | |
222 | kwargs['device'] = device |
|
226 | kwargs['device'] = device | |
223 | kwargs['device_keys'] = ['device_type', |
|
227 | kwargs['device_keys'] = ['device_type', | |
224 | 'ip_address', 'port_address', 'description'] |
|
228 | 'ip_address', 'port_address', 'description'] | |
225 |
|
229 | |||
226 | kwargs['title'] = 'Device' |
|
230 | kwargs['title'] = 'Device' | |
227 | kwargs['suptitle'] = 'Details' |
|
231 | kwargs['suptitle'] = 'Details' | |
228 | kwargs['menu_devices'] = 'active' |
|
232 | kwargs['menu_devices'] = 'active' | |
229 |
|
233 | |||
230 | return render(request, 'device.html', kwargs) |
|
234 | return render(request, 'device.html', kwargs) | |
231 |
|
235 | |||
232 |
|
236 | |||
233 | @login_required |
|
237 | @login_required | |
234 | def device_new(request): |
|
238 | def device_new(request): | |
235 |
|
239 | |||
236 | if request.method == 'GET': |
|
240 | if request.method == 'GET': | |
237 | form = DeviceForm() |
|
241 | form = DeviceForm() | |
238 |
|
242 | |||
239 | if request.method == 'POST': |
|
243 | if request.method == 'POST': | |
240 | form = DeviceForm(request.POST) |
|
244 | form = DeviceForm(request.POST) | |
241 |
|
245 | |||
242 | if form.is_valid(): |
|
246 | if form.is_valid(): | |
243 | form.save() |
|
247 | form.save() | |
244 | return redirect('url_devices') |
|
248 | return redirect('url_devices') | |
245 |
|
249 | |||
246 | kwargs = {} |
|
250 | kwargs = {} | |
247 | kwargs['form'] = form |
|
251 | kwargs['form'] = form | |
248 | kwargs['title'] = 'Device' |
|
252 | kwargs['title'] = 'Device' | |
249 | kwargs['suptitle'] = 'New' |
|
253 | kwargs['suptitle'] = 'New' | |
250 | kwargs['button'] = 'Create' |
|
254 | kwargs['button'] = 'Create' | |
251 | kwargs['menu_devices'] = 'active' |
|
255 | kwargs['menu_devices'] = 'active' | |
252 |
|
256 | |||
253 | return render(request, 'base_edit.html', kwargs) |
|
257 | return render(request, 'base_edit.html', kwargs) | |
254 |
|
258 | |||
255 |
|
259 | |||
256 | @login_required |
|
260 | @login_required | |
257 | def device_edit(request, id_dev): |
|
261 | def device_edit(request, id_dev): | |
258 |
|
262 | |||
259 | device = get_object_or_404(Device, pk=id_dev) |
|
263 | device = get_object_or_404(Device, pk=id_dev) | |
260 |
|
264 | |||
261 | if request.method == 'GET': |
|
265 | if request.method == 'GET': | |
262 | form = DeviceForm(instance=device) |
|
266 | form = DeviceForm(instance=device) | |
263 |
|
267 | |||
264 | if request.method == 'POST': |
|
268 | if request.method == 'POST': | |
265 | form = DeviceForm(request.POST, instance=device) |
|
269 | form = DeviceForm(request.POST, instance=device) | |
266 |
|
270 | |||
267 | if form.is_valid(): |
|
271 | if form.is_valid(): | |
268 | form.save() |
|
272 | form.save() | |
269 | return redirect(device.get_absolute_url()) |
|
273 | return redirect(device.get_absolute_url()) | |
270 |
|
274 | |||
271 | kwargs = {} |
|
275 | kwargs = {} | |
272 | kwargs['form'] = form |
|
276 | kwargs['form'] = form | |
273 | kwargs['title'] = 'Device' |
|
277 | kwargs['title'] = 'Device' | |
274 | kwargs['suptitle'] = 'Edit' |
|
278 | kwargs['suptitle'] = 'Edit' | |
275 | kwargs['button'] = 'Update' |
|
279 | kwargs['button'] = 'Update' | |
276 | kwargs['menu_devices'] = 'active' |
|
280 | kwargs['menu_devices'] = 'active' | |
277 |
|
281 | |||
278 | return render(request, 'base_edit.html', kwargs) |
|
282 | return render(request, 'base_edit.html', kwargs) | |
279 |
|
283 | |||
280 |
|
284 | |||
281 | @login_required |
|
285 | @login_required | |
282 | def device_delete(request, id_dev): |
|
286 | def device_delete(request, id_dev): | |
283 |
|
287 | |||
284 | device = get_object_or_404(Device, pk=id_dev) |
|
288 | device = get_object_or_404(Device, pk=id_dev) | |
285 |
|
289 | |||
286 | if request.method == 'POST': |
|
290 | if request.method == 'POST': | |
287 |
|
291 | |||
288 | if is_developer(request.user): |
|
292 | if is_developer(request.user): | |
289 | device.delete() |
|
293 | device.delete() | |
290 | return redirect('url_devices') |
|
294 | return redirect('url_devices') | |
291 |
|
295 | |||
292 | messages.error(request, 'Not enough permission to delete this object') |
|
296 | messages.error(request, 'Not enough permission to delete this object') | |
293 | return redirect(device.get_absolute_url()) |
|
297 | return redirect(device.get_absolute_url()) | |
294 |
|
298 | |||
295 | kwargs = { |
|
299 | kwargs = { | |
296 | 'title': 'Delete', |
|
300 | 'title': 'Delete', | |
297 | 'suptitle': 'Device', |
|
301 | 'suptitle': 'Device', | |
298 | 'object': device, |
|
302 | 'object': device, | |
299 | 'delete': True |
|
303 | 'delete': True | |
300 | } |
|
304 | } | |
301 | kwargs['menu_devices'] = 'active' |
|
305 | kwargs['menu_devices'] = 'active' | |
302 |
|
306 | |||
303 | return render(request, 'confirm.html', kwargs) |
|
307 | return render(request, 'confirm.html', kwargs) | |
304 |
|
308 | |||
305 |
|
309 | |||
306 | @login_required |
|
310 | @login_required | |
307 | def device_change_ip(request, id_dev): |
|
311 | def device_change_ip(request, id_dev): | |
308 |
|
312 | |||
309 | device = get_object_or_404(Device, pk=id_dev) |
|
313 | device = get_object_or_404(Device, pk=id_dev) | |
310 |
|
314 | |||
311 | if request.method == 'POST': |
|
315 | if request.method == 'POST': | |
312 |
|
316 | |||
313 | if is_developer(request.user): |
|
317 | if is_developer(request.user): | |
314 | device.change_ip(**request.POST.dict()) |
|
318 | device.change_ip(**request.POST.dict()) | |
315 | level, message = device.message.split('|') |
|
319 | level, message = device.message.split('|') | |
316 | messages.add_message(request, level, message) |
|
320 | messages.add_message(request, level, message) | |
317 | else: |
|
321 | else: | |
318 | messages.error( |
|
322 | messages.error( | |
319 | request, 'Not enough permission to delete this object') |
|
323 | request, 'Not enough permission to delete this object') | |
320 | return redirect(device.get_absolute_url()) |
|
324 | return redirect(device.get_absolute_url()) | |
321 |
|
325 | |||
322 | kwargs = { |
|
326 | kwargs = { | |
323 | 'title': 'Device', |
|
327 | 'title': 'Device', | |
324 | 'suptitle': 'Change IP', |
|
328 | 'suptitle': 'Change IP', | |
325 | 'object': device, |
|
329 | 'object': device, | |
326 | 'previous': device.get_absolute_url(), |
|
330 | 'previous': device.get_absolute_url(), | |
327 | 'form': ChangeIpForm(initial={'ip_address': device.ip_address}), |
|
331 | 'form': ChangeIpForm(initial={'ip_address': device.ip_address}), | |
328 | 'message': ' ', |
|
332 | 'message': ' ', | |
329 | } |
|
333 | } | |
330 | kwargs['menu_devices'] = 'active' |
|
334 | kwargs['menu_devices'] = 'active' | |
331 |
|
335 | |||
332 | return render(request, 'confirm.html', kwargs) |
|
336 | return render(request, 'confirm.html', kwargs) | |
333 |
|
337 | |||
334 |
|
338 | |||
335 | def campaigns(request): |
|
339 | def campaigns(request): | |
336 |
|
340 | |||
337 | page = request.GET.get('page') |
|
341 | page = request.GET.get('page') | |
338 | order = ('-start_date',) |
|
342 | order = ('-start_date',) | |
339 | filters = request.GET.copy() |
|
343 | filters = request.GET.copy() | |
340 |
|
344 | |||
341 | kwargs = get_paginator(Campaign, page, order, filters) |
|
345 | kwargs = get_paginator(Campaign, page, order, filters) | |
342 |
|
346 | |||
343 | form = FilterForm(initial=request.GET, extra_fields=[ |
|
347 | form = FilterForm(initial=request.GET, extra_fields=[ | |
344 | 'range_date', 'tags', 'template']) |
|
348 | 'range_date', 'tags', 'template']) | |
345 | kwargs['keys'] = ['name', 'start_date', 'end_date', 'actions'] |
|
349 | kwargs['keys'] = ['name', 'start_date', 'end_date', 'actions'] | |
346 | kwargs['title'] = 'Campaign' |
|
350 | kwargs['title'] = 'Campaign' | |
347 | kwargs['suptitle'] = 'List' |
|
351 | kwargs['suptitle'] = 'List' | |
348 | kwargs['no_sidebar'] = True |
|
352 | kwargs['no_sidebar'] = True | |
349 | kwargs['form'] = form |
|
353 | kwargs['form'] = form | |
350 | kwargs['add_url'] = reverse('url_add_campaign') |
|
354 | kwargs['add_url'] = reverse('url_add_campaign') | |
351 | filters.pop('page', None) |
|
355 | filters.pop('page', None) | |
352 | kwargs['q'] = urlencode(filters) |
|
356 | kwargs['q'] = urlencode(filters) | |
353 | kwargs['menu_campaigns'] = 'active' |
|
357 | kwargs['menu_campaigns'] = 'active' | |
354 |
|
358 | |||
355 | return render(request, 'base_list.html', kwargs) |
|
359 | return render(request, 'base_list.html', kwargs) | |
356 |
|
360 | |||
357 |
|
361 | |||
358 | def campaign(request, id_camp): |
|
362 | def campaign(request, id_camp): | |
359 |
|
363 | |||
360 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
364 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
361 | experiments = Experiment.objects.filter(campaign=campaign) |
|
365 | experiments = Experiment.objects.filter(campaign=campaign) | |
362 |
|
366 | |||
363 | form = CampaignForm(instance=campaign) |
|
367 | form = CampaignForm(instance=campaign) | |
364 |
|
368 | |||
365 | kwargs = {} |
|
369 | kwargs = {} | |
366 | kwargs['campaign'] = campaign |
|
370 | kwargs['campaign'] = campaign | |
367 | kwargs['campaign_keys'] = ['template', 'name', |
|
371 | kwargs['campaign_keys'] = ['template', 'name', | |
368 | 'start_date', 'end_date', 'tags', 'description'] |
|
372 | 'start_date', 'end_date', 'tags', 'description'] | |
369 |
|
373 | |||
370 | kwargs['experiments'] = experiments |
|
374 | kwargs['experiments'] = experiments | |
371 | kwargs['experiment_keys'] = [ |
|
375 | kwargs['experiment_keys'] = [ | |
372 | 'name', 'radar_system', 'start_time', 'end_time'] |
|
376 | 'name', 'radar_system', 'start_time', 'end_time'] | |
373 |
|
377 | |||
374 | kwargs['title'] = 'Campaign' |
|
378 | kwargs['title'] = 'Campaign' | |
375 | kwargs['suptitle'] = 'Details' |
|
379 | kwargs['suptitle'] = 'Details' | |
376 |
|
380 | |||
377 | kwargs['form'] = form |
|
381 | kwargs['form'] = form | |
378 | kwargs['button'] = 'Add Experiment' |
|
382 | kwargs['button'] = 'Add Experiment' | |
379 | kwargs['menu_campaigns'] = 'active' |
|
383 | kwargs['menu_campaigns'] = 'active' | |
380 |
|
384 | |||
381 | return render(request, 'campaign.html', kwargs) |
|
385 | return render(request, 'campaign.html', kwargs) | |
382 |
|
386 | |||
383 |
|
387 | |||
384 | @login_required |
|
388 | @login_required | |
385 | def campaign_new(request): |
|
389 | def campaign_new(request): | |
386 |
|
390 | |||
387 | kwargs = {} |
|
391 | kwargs = {} | |
388 |
|
392 | |||
389 | if request.method == 'GET': |
|
393 | if request.method == 'GET': | |
390 |
|
394 | |||
391 | if 'template' in request.GET: |
|
395 | if 'template' in request.GET: | |
392 | if request.GET['template'] == '0': |
|
396 | if request.GET['template'] == '0': | |
393 | form = NewForm(initial={'create_from': 2}, |
|
397 | form = NewForm(initial={'create_from': 2}, | |
394 | template_choices=Campaign.objects.filter(template=True).values_list('id', 'name')) |
|
398 | template_choices=Campaign.objects.filter(template=True).values_list('id', 'name')) | |
395 | else: |
|
399 | else: | |
396 | kwargs['button'] = 'Create' |
|
400 | kwargs['button'] = 'Create' | |
397 | kwargs['experiments'] = Configuration.objects.filter( |
|
401 | kwargs['experiments'] = Configuration.objects.filter( | |
398 | experiment=request.GET['template']) |
|
402 | experiment=request.GET['template']) | |
399 | kwargs['experiment_keys'] = ['name', 'start_time', 'end_time'] |
|
403 | kwargs['experiment_keys'] = ['name', 'start_time', 'end_time'] | |
400 | camp = Campaign.objects.get(pk=request.GET['template']) |
|
404 | camp = Campaign.objects.get(pk=request.GET['template']) | |
401 | form = CampaignForm(instance=camp, |
|
405 | form = CampaignForm(instance=camp, | |
402 | initial={'name': '{}_{:%Y%m%d}'.format(camp.name, datetime.now()), |
|
406 | initial={'name': '{}_{:%Y%m%d}'.format(camp.name, datetime.now()), | |
403 | 'template': False}) |
|
407 | 'template': False}) | |
404 | elif 'blank' in request.GET: |
|
408 | elif 'blank' in request.GET: | |
405 | kwargs['button'] = 'Create' |
|
409 | kwargs['button'] = 'Create' | |
406 | form = CampaignForm() |
|
410 | form = CampaignForm() | |
407 | else: |
|
411 | else: | |
408 | form = NewForm() |
|
412 | form = NewForm() | |
409 |
|
413 | |||
410 | if request.method == 'POST': |
|
414 | if request.method == 'POST': | |
411 | kwargs['button'] = 'Create' |
|
415 | kwargs['button'] = 'Create' | |
412 | post = request.POST.copy() |
|
416 | post = request.POST.copy() | |
413 | experiments = [] |
|
417 | experiments = [] | |
414 |
|
418 | |||
415 | for id_exp in post.getlist('experiments'): |
|
419 | for id_exp in post.getlist('experiments'): | |
416 | exp = Experiment.objects.get(pk=id_exp) |
|
420 | exp = Experiment.objects.get(pk=id_exp) | |
417 | new_exp = exp.clone(template=False) |
|
421 | new_exp = exp.clone(template=False) | |
418 | experiments.append(new_exp) |
|
422 | experiments.append(new_exp) | |
419 |
|
423 | |||
420 | post.setlist('experiments', []) |
|
424 | post.setlist('experiments', []) | |
421 |
|
425 | |||
422 | form = CampaignForm(post) |
|
426 | form = CampaignForm(post) | |
423 |
|
427 | |||
424 | if form.is_valid(): |
|
428 | if form.is_valid(): | |
425 | campaign = form.save(commit=False) |
|
429 | campaign = form.save(commit=False) | |
426 | campaign.author = request.user |
|
430 | campaign.author = request.user | |
427 | for exp in experiments: |
|
431 | for exp in experiments: | |
428 | campaign.experiments.add(exp) |
|
432 | campaign.experiments.add(exp) | |
429 | campaign.save() |
|
433 | campaign.save() | |
430 | return redirect('url_campaign', id_camp=campaign.id) |
|
434 | return redirect('url_campaign', id_camp=campaign.id) | |
431 |
|
435 | |||
432 | kwargs['form'] = form |
|
436 | kwargs['form'] = form | |
433 | kwargs['title'] = 'Campaign' |
|
437 | kwargs['title'] = 'Campaign' | |
434 | kwargs['suptitle'] = 'New' |
|
438 | kwargs['suptitle'] = 'New' | |
435 | kwargs['menu_campaigns'] = 'active' |
|
439 | kwargs['menu_campaigns'] = 'active' | |
436 |
|
440 | |||
437 | return render(request, 'campaign_edit.html', kwargs) |
|
441 | return render(request, 'campaign_edit.html', kwargs) | |
438 |
|
442 | |||
439 |
|
443 | |||
440 | @login_required |
|
444 | @login_required | |
441 | def campaign_edit(request, id_camp): |
|
445 | def campaign_edit(request, id_camp): | |
442 |
|
446 | |||
443 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
447 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
444 |
|
448 | |||
445 | if request.method == 'GET': |
|
449 | if request.method == 'GET': | |
446 | form = CampaignForm(instance=campaign) |
|
450 | form = CampaignForm(instance=campaign) | |
447 |
|
451 | |||
448 | if request.method == 'POST': |
|
452 | if request.method == 'POST': | |
449 | exps = campaign.experiments.all().values_list('pk', flat=True) |
|
453 | exps = campaign.experiments.all().values_list('pk', flat=True) | |
450 | post = request.POST.copy() |
|
454 | post = request.POST.copy() | |
451 | new_exps = post.getlist('experiments') |
|
455 | new_exps = post.getlist('experiments') | |
452 | post.setlist('experiments', []) |
|
456 | post.setlist('experiments', []) | |
453 | form = CampaignForm(post, instance=campaign) |
|
457 | form = CampaignForm(post, instance=campaign) | |
454 |
|
458 | |||
455 | if form.is_valid(): |
|
459 | if form.is_valid(): | |
456 | camp = form.save() |
|
460 | camp = form.save() | |
457 | for id_exp in new_exps: |
|
461 | for id_exp in new_exps: | |
458 | if int(id_exp) in exps: |
|
462 | if int(id_exp) in exps: | |
459 | exps.pop(id_exp) |
|
463 | exps.pop(id_exp) | |
460 | else: |
|
464 | else: | |
461 | exp = Experiment.objects.get(pk=id_exp) |
|
465 | exp = Experiment.objects.get(pk=id_exp) | |
462 | if exp.template: |
|
466 | if exp.template: | |
463 | camp.experiments.add(exp.clone(template=False)) |
|
467 | camp.experiments.add(exp.clone(template=False)) | |
464 | else: |
|
468 | else: | |
465 | camp.experiments.add(exp) |
|
469 | camp.experiments.add(exp) | |
466 |
|
470 | |||
467 | for id_exp in exps: |
|
471 | for id_exp in exps: | |
468 | camp.experiments.remove(Experiment.objects.get(pk=id_exp)) |
|
472 | camp.experiments.remove(Experiment.objects.get(pk=id_exp)) | |
469 |
|
473 | |||
470 | return redirect('url_campaign', id_camp=id_camp) |
|
474 | return redirect('url_campaign', id_camp=id_camp) | |
471 |
|
475 | |||
472 | kwargs = {} |
|
476 | kwargs = {} | |
473 | kwargs['form'] = form |
|
477 | kwargs['form'] = form | |
474 | kwargs['title'] = 'Campaign' |
|
478 | kwargs['title'] = 'Campaign' | |
475 | kwargs['suptitle'] = 'Edit' |
|
479 | kwargs['suptitle'] = 'Edit' | |
476 | kwargs['button'] = 'Update' |
|
480 | kwargs['button'] = 'Update' | |
477 | kwargs['menu_campaigns'] = 'active' |
|
481 | kwargs['menu_campaigns'] = 'active' | |
478 |
|
482 | |||
479 | return render(request, 'campaign_edit.html', kwargs) |
|
483 | return render(request, 'campaign_edit.html', kwargs) | |
480 |
|
484 | |||
481 |
|
485 | |||
482 | @login_required |
|
486 | @login_required | |
483 | def campaign_delete(request, id_camp): |
|
487 | def campaign_delete(request, id_camp): | |
484 |
|
488 | |||
485 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
489 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
486 |
|
490 | |||
487 | if request.method == 'POST': |
|
491 | if request.method == 'POST': | |
488 | if is_developer(request.user): |
|
492 | if is_developer(request.user): | |
489 |
|
493 | |||
490 | for exp in campaign.experiments.all(): |
|
494 | for exp in campaign.experiments.all(): | |
491 | for conf in Configuration.objects.filter(experiment=exp): |
|
495 | for conf in Configuration.objects.filter(experiment=exp): | |
492 | conf.delete() |
|
496 | conf.delete() | |
493 | exp.delete() |
|
497 | exp.delete() | |
494 | campaign.delete() |
|
498 | campaign.delete() | |
495 |
|
499 | |||
496 | return redirect('url_campaigns') |
|
500 | return redirect('url_campaigns') | |
497 |
|
501 | |||
498 | messages.error(request, 'Not enough permission to delete this object') |
|
502 | messages.error(request, 'Not enough permission to delete this object') | |
499 | return redirect(campaign.get_absolute_url()) |
|
503 | return redirect(campaign.get_absolute_url()) | |
500 |
|
504 | |||
501 | kwargs = { |
|
505 | kwargs = { | |
502 | 'title': 'Delete', |
|
506 | 'title': 'Delete', | |
503 | 'suptitle': 'Campaign', |
|
507 | 'suptitle': 'Campaign', | |
504 | 'object': campaign, |
|
508 | 'object': campaign, | |
505 | 'delete': True |
|
509 | 'delete': True | |
506 | } |
|
510 | } | |
507 | kwargs['menu_campaigns'] = 'active' |
|
511 | kwargs['menu_campaigns'] = 'active' | |
508 |
|
512 | |||
509 | return render(request, 'confirm.html', kwargs) |
|
513 | return render(request, 'confirm.html', kwargs) | |
510 |
|
514 | |||
511 |
|
515 | |||
512 | @login_required |
|
516 | @login_required | |
513 | def campaign_export(request, id_camp): |
|
517 | def campaign_export(request, id_camp): | |
514 |
|
518 | |||
515 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
519 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
516 | content = campaign.parms_to_dict() |
|
520 | content = campaign.parms_to_dict() | |
517 | content_type = 'application/json' |
|
521 | content_type = 'application/json' | |
518 | filename = '%s_%s.json' % (campaign.name, campaign.id) |
|
522 | filename = '%s_%s.json' % (campaign.name, campaign.id) | |
519 |
|
523 | |||
520 | response = HttpResponse(content_type=content_type) |
|
524 | response = HttpResponse(content_type=content_type) | |
521 | response['Content-Disposition'] = 'attachment; filename="%s"' % filename |
|
525 | response['Content-Disposition'] = 'attachment; filename="%s"' % filename | |
522 | response.write(json.dumps(content, indent=2)) |
|
526 | response.write(json.dumps(content, indent=2)) | |
523 |
|
527 | |||
524 | return response |
|
528 | return response | |
525 |
|
529 | |||
526 |
|
530 | |||
527 | @login_required |
|
531 | @login_required | |
528 | def campaign_import(request, id_camp): |
|
532 | def campaign_import(request, id_camp): | |
529 |
|
533 | |||
530 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
534 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
531 |
|
535 | |||
532 | if request.method == 'GET': |
|
536 | if request.method == 'GET': | |
533 | file_form = UploadFileForm() |
|
537 | file_form = UploadFileForm() | |
534 |
|
538 | |||
535 | if request.method == 'POST': |
|
539 | if request.method == 'POST': | |
536 | file_form = UploadFileForm(request.POST, request.FILES) |
|
540 | file_form = UploadFileForm(request.POST, request.FILES) | |
537 |
|
541 | |||
538 | if file_form.is_valid(): |
|
542 | if file_form.is_valid(): | |
539 | new_camp = campaign.dict_to_parms( |
|
543 | new_camp = campaign.dict_to_parms( | |
540 | json.load(request.FILES['file']), CONF_MODELS) |
|
544 | json.load(request.FILES['file']), CONF_MODELS) | |
541 | messages.success( |
|
545 | messages.success( | |
542 | request, "Parameters imported from: '%s'." % request.FILES['file'].name) |
|
546 | request, "Parameters imported from: '%s'." % request.FILES['file'].name) | |
543 | return redirect(new_camp.get_absolute_url_edit()) |
|
547 | return redirect(new_camp.get_absolute_url_edit()) | |
544 |
|
548 | |||
545 | messages.error(request, "Could not import parameters from file") |
|
549 | messages.error(request, "Could not import parameters from file") | |
546 |
|
550 | |||
547 | kwargs = {} |
|
551 | kwargs = {} | |
548 | kwargs['title'] = 'Campaign' |
|
552 | kwargs['title'] = 'Campaign' | |
549 | kwargs['form'] = file_form |
|
553 | kwargs['form'] = file_form | |
550 | kwargs['suptitle'] = 'Importing file' |
|
554 | kwargs['suptitle'] = 'Importing file' | |
551 | kwargs['button'] = 'Import' |
|
555 | kwargs['button'] = 'Import' | |
552 | kwargs['menu_campaigns'] = 'active' |
|
556 | kwargs['menu_campaigns'] = 'active' | |
553 |
|
557 | |||
554 | return render(request, 'campaign_import.html', kwargs) |
|
558 | return render(request, 'campaign_import.html', kwargs) | |
555 |
|
559 | |||
556 |
|
560 | |||
557 | def experiments(request): |
|
561 | def experiments(request): | |
558 |
|
562 | |||
559 | page = request.GET.get('page') |
|
563 | page = request.GET.get('page') | |
560 | order = ('location',) |
|
564 | order = ('location',) | |
561 | filters = request.GET.copy() |
|
565 | filters = request.GET.copy() | |
562 |
|
566 | |||
563 | if 'my experiments' in filters: |
|
567 | if 'my experiments' in filters: | |
564 | filters.pop('my experiments', None) |
|
568 | filters.pop('my experiments', None) | |
565 | filters['mine'] = request.user.id |
|
569 | filters['mine'] = request.user.id | |
566 |
|
570 | |||
567 | kwargs = get_paginator(Experiment, page, order, filters) |
|
571 | kwargs = get_paginator(Experiment, page, order, filters) | |
568 |
|
572 | |||
569 | fields = ['tags', 'template'] |
|
573 | fields = ['tags', 'template'] | |
570 | if request.user.is_authenticated: |
|
574 | if request.user.is_authenticated: | |
571 | fields.append('my experiments') |
|
575 | fields.append('my experiments') | |
572 |
|
576 | |||
573 | form = FilterForm(initial=request.GET, extra_fields=fields) |
|
577 | form = FilterForm(initial=request.GET, extra_fields=fields) | |
574 |
|
578 | |||
575 | kwargs['keys'] = ['name', 'radar_system', |
|
579 | kwargs['keys'] = ['name', 'radar_system', | |
576 | 'start_time', 'end_time', 'actions'] |
|
580 | 'start_time', 'end_time', 'actions'] | |
577 | kwargs['title'] = 'Experiment' |
|
581 | kwargs['title'] = 'Experiment' | |
578 | kwargs['suptitle'] = 'List' |
|
582 | kwargs['suptitle'] = 'List' | |
579 | kwargs['no_sidebar'] = True |
|
583 | kwargs['no_sidebar'] = True | |
580 | kwargs['form'] = form |
|
584 | kwargs['form'] = form | |
581 | kwargs['add_url'] = reverse('url_add_experiment') |
|
585 | kwargs['add_url'] = reverse('url_add_experiment') | |
582 | filters = request.GET.copy() |
|
586 | filters = request.GET.copy() | |
583 | filters.pop('page', None) |
|
587 | filters.pop('page', None) | |
584 | kwargs['q'] = urlencode(filters) |
|
588 | kwargs['q'] = urlencode(filters) | |
585 | kwargs['menu_experiments'] = 'active' |
|
589 | kwargs['menu_experiments'] = 'active' | |
586 |
|
590 | |||
587 | return render(request, 'base_list.html', kwargs) |
|
591 | return render(request, 'base_list.html', kwargs) | |
588 |
|
592 | |||
589 |
|
593 | |||
590 | def experiment(request, id_exp): |
|
594 | def experiment(request, id_exp): | |
591 |
|
595 | |||
592 | experiment = get_object_or_404(Experiment, pk=id_exp) |
|
596 | experiment = get_object_or_404(Experiment, pk=id_exp) | |
593 |
|
597 | |||
594 | configurations = Configuration.objects.filter( |
|
598 | configurations = Configuration.objects.filter( | |
595 | experiment=experiment, type=0) |
|
599 | experiment=experiment, type=0) | |
596 |
|
600 | |||
597 | kwargs = {} |
|
601 | kwargs = {} | |
598 |
|
602 | |||
599 | kwargs['experiment_keys'] = ['template', 'radar_system', |
|
603 | kwargs['experiment_keys'] = ['template', 'radar_system', | |
600 | 'name', 'start_time', 'end_time'] |
|
604 | 'name', 'start_time', 'end_time'] | |
601 | kwargs['experiment'] = experiment |
|
605 | kwargs['experiment'] = experiment | |
602 | kwargs['configuration_keys'] = ['name', 'device__ip_address', |
|
606 | kwargs['configuration_keys'] = ['name', 'device__ip_address', | |
603 | 'device__port_address', 'device__status'] |
|
607 | 'device__port_address', 'device__status'] | |
604 | kwargs['configurations'] = configurations |
|
608 | kwargs['configurations'] = configurations | |
605 | kwargs['title'] = 'Experiment' |
|
609 | kwargs['title'] = 'Experiment' | |
606 | kwargs['suptitle'] = 'Details' |
|
610 | kwargs['suptitle'] = 'Details' | |
607 | kwargs['button'] = 'Add Configuration' |
|
611 | kwargs['button'] = 'Add Configuration' | |
608 | kwargs['menu_experiments'] = 'active' |
|
612 | kwargs['menu_experiments'] = 'active' | |
609 |
|
613 | |||
610 | ###### SIDEBAR ###### |
|
614 | ###### SIDEBAR ###### | |
611 | kwargs.update(sidebar(experiment=experiment)) |
|
615 | kwargs.update(sidebar(experiment=experiment)) | |
612 |
|
616 | |||
613 | return render(request, 'experiment.html', kwargs) |
|
617 | return render(request, 'experiment.html', kwargs) | |
614 |
|
618 | |||
615 |
|
619 | |||
616 | @login_required |
|
620 | @login_required | |
617 | def experiment_new(request, id_camp=None): |
|
621 | def experiment_new(request, id_camp=None): | |
618 |
|
622 | |||
619 | if not is_developer(request.user): |
|
623 | if not is_developer(request.user): | |
620 | messages.error( |
|
624 | messages.error( | |
621 | request, 'Developer required, to create new Experiments') |
|
625 | request, 'Developer required, to create new Experiments') | |
622 | return redirect('index') |
|
626 | return redirect('index') | |
623 | kwargs = {} |
|
627 | kwargs = {} | |
624 |
|
628 | |||
625 | if request.method == 'GET': |
|
629 | if request.method == 'GET': | |
626 | if 'template' in request.GET: |
|
630 | if 'template' in request.GET: | |
627 | if request.GET['template'] == '0': |
|
631 | if request.GET['template'] == '0': | |
628 | form = NewForm(initial={'create_from': 2}, |
|
632 | form = NewForm(initial={'create_from': 2}, | |
629 | template_choices=Experiment.objects.filter(template=True).values_list('id', 'name')) |
|
633 | template_choices=Experiment.objects.filter(template=True).values_list('id', 'name')) | |
630 | else: |
|
634 | else: | |
631 | kwargs['button'] = 'Create' |
|
635 | kwargs['button'] = 'Create' | |
632 | kwargs['configurations'] = Configuration.objects.filter( |
|
636 | kwargs['configurations'] = Configuration.objects.filter( | |
633 | experiment=request.GET['template']) |
|
637 | experiment=request.GET['template']) | |
634 | kwargs['configuration_keys'] = ['name', 'device__name', |
|
638 | kwargs['configuration_keys'] = ['name', 'device__name', | |
635 | 'device__ip_address', 'device__port_address'] |
|
639 | 'device__ip_address', 'device__port_address'] | |
636 | exp = Experiment.objects.get(pk=request.GET['template']) |
|
640 | exp = Experiment.objects.get(pk=request.GET['template']) | |
637 | form = ExperimentForm(instance=exp, |
|
641 | form = ExperimentForm(instance=exp, | |
638 | initial={'name': '{}_{:%y%m%d}'.format(exp.name, datetime.now()), |
|
642 | initial={'name': '{}_{:%y%m%d}'.format(exp.name, datetime.now()), | |
639 | 'template': False}) |
|
643 | 'template': False}) | |
640 | elif 'blank' in request.GET: |
|
644 | elif 'blank' in request.GET: | |
641 | kwargs['button'] = 'Create' |
|
645 | kwargs['button'] = 'Create' | |
642 | form = ExperimentForm() |
|
646 | form = ExperimentForm() | |
643 | else: |
|
647 | else: | |
644 | form = NewForm() |
|
648 | form = NewForm() | |
645 |
|
649 | |||
646 | if request.method == 'POST': |
|
650 | if request.method == 'POST': | |
647 | form = ExperimentForm(request.POST) |
|
651 | form = ExperimentForm(request.POST) | |
648 | if form.is_valid(): |
|
652 | if form.is_valid(): | |
649 | experiment = form.save(commit=False) |
|
653 | experiment = form.save(commit=False) | |
650 | experiment.author = request.user |
|
654 | experiment.author = request.user | |
651 | experiment.save() |
|
655 | experiment.save() | |
652 |
|
656 | |||
653 | if 'template' in request.GET: |
|
657 | if 'template' in request.GET: | |
654 | configurations = Configuration.objects.filter( |
|
658 | configurations = Configuration.objects.filter( | |
655 | experiment=request.GET['template'], type=0) |
|
659 | experiment=request.GET['template'], type=0) | |
656 | for conf in configurations: |
|
660 | for conf in configurations: | |
657 | conf.clone(experiment=experiment, template=False) |
|
661 | conf.clone(experiment=experiment, template=False) | |
658 |
|
662 | |||
659 | return redirect('url_experiment', id_exp=experiment.id) |
|
663 | return redirect('url_experiment', id_exp=experiment.id) | |
660 |
|
664 | |||
661 | kwargs['form'] = form |
|
665 | kwargs['form'] = form | |
662 | kwargs['title'] = 'Experiment' |
|
666 | kwargs['title'] = 'Experiment' | |
663 | kwargs['suptitle'] = 'New' |
|
667 | kwargs['suptitle'] = 'New' | |
664 | kwargs['menu_experiments'] = 'active' |
|
668 | kwargs['menu_experiments'] = 'active' | |
665 |
|
669 | |||
666 | return render(request, 'experiment_edit.html', kwargs) |
|
670 | return render(request, 'experiment_edit.html', kwargs) | |
667 |
|
671 | |||
668 |
|
672 | |||
669 | @login_required |
|
673 | @login_required | |
670 | def experiment_edit(request, id_exp): |
|
674 | def experiment_edit(request, id_exp): | |
671 |
|
675 | |||
672 | experiment = get_object_or_404(Experiment, pk=id_exp) |
|
676 | experiment = get_object_or_404(Experiment, pk=id_exp) | |
673 |
|
677 | |||
674 | if request.method == 'GET': |
|
678 | if request.method == 'GET': | |
675 | form = ExperimentForm(instance=experiment) |
|
679 | form = ExperimentForm(instance=experiment) | |
676 |
|
680 | |||
677 | if request.method == 'POST': |
|
681 | if request.method == 'POST': | |
678 | form = ExperimentForm(request.POST, instance=experiment) |
|
682 | form = ExperimentForm(request.POST, instance=experiment) | |
679 |
|
683 | |||
680 | if form.is_valid(): |
|
684 | if form.is_valid(): | |
681 | experiment = form.save() |
|
685 | experiment = form.save() | |
682 | return redirect('url_experiment', id_exp=experiment.id) |
|
686 | return redirect('url_experiment', id_exp=experiment.id) | |
683 |
|
687 | |||
684 | kwargs = {} |
|
688 | kwargs = {} | |
685 | kwargs['form'] = form |
|
689 | kwargs['form'] = form | |
686 | kwargs['title'] = 'Experiment' |
|
690 | kwargs['title'] = 'Experiment' | |
687 | kwargs['suptitle'] = 'Edit' |
|
691 | kwargs['suptitle'] = 'Edit' | |
688 | kwargs['button'] = 'Update' |
|
692 | kwargs['button'] = 'Update' | |
689 | kwargs['menu_experiments'] = 'active' |
|
693 | kwargs['menu_experiments'] = 'active' | |
690 |
|
694 | |||
691 | return render(request, 'experiment_edit.html', kwargs) |
|
695 | return render(request, 'experiment_edit.html', kwargs) | |
692 |
|
696 | |||
693 |
|
697 | |||
694 | @login_required |
|
698 | @login_required | |
695 | def experiment_delete(request, id_exp): |
|
699 | def experiment_delete(request, id_exp): | |
696 |
|
700 | |||
697 | experiment = get_object_or_404(Experiment, pk=id_exp) |
|
701 | experiment = get_object_or_404(Experiment, pk=id_exp) | |
698 |
|
702 | |||
699 | if request.method == 'POST': |
|
703 | if request.method == 'POST': | |
700 | if is_developer(request.user): |
|
704 | if is_developer(request.user): | |
701 | for conf in Configuration.objects.filter(experiment=experiment): |
|
705 | for conf in Configuration.objects.filter(experiment=experiment): | |
702 | conf.delete() |
|
706 | conf.delete() | |
703 | experiment.delete() |
|
707 | experiment.delete() | |
704 | return redirect('url_experiments') |
|
708 | return redirect('url_experiments') | |
705 |
|
709 | |||
706 | messages.error(request, 'Not enough permission to delete this object') |
|
710 | messages.error(request, 'Not enough permission to delete this object') | |
707 | return redirect(experiment.get_absolute_url()) |
|
711 | return redirect(experiment.get_absolute_url()) | |
708 |
|
712 | |||
709 | kwargs = { |
|
713 | kwargs = { | |
710 | 'title': 'Delete', |
|
714 | 'title': 'Delete', | |
711 | 'suptitle': 'Experiment', |
|
715 | 'suptitle': 'Experiment', | |
712 | 'object': experiment, |
|
716 | 'object': experiment, | |
713 | 'delete': True |
|
717 | 'delete': True | |
714 | } |
|
718 | } | |
715 |
|
719 | |||
716 | return render(request, 'confirm.html', kwargs) |
|
720 | return render(request, 'confirm.html', kwargs) | |
717 |
|
721 | |||
718 |
|
722 | |||
719 | @login_required |
|
723 | @login_required | |
720 | def experiment_export(request, id_exp): |
|
724 | def experiment_export(request, id_exp): | |
721 |
|
725 | |||
722 | experiment = get_object_or_404(Experiment, pk=id_exp) |
|
726 | experiment = get_object_or_404(Experiment, pk=id_exp) | |
723 | content = experiment.parms_to_dict() |
|
727 | content = experiment.parms_to_dict() | |
724 | content_type = 'application/json' |
|
728 | content_type = 'application/json' | |
725 | filename = '%s_%s.json' % (experiment.name, experiment.id) |
|
729 | filename = '%s_%s.json' % (experiment.name, experiment.id) | |
726 |
|
730 | |||
727 | response = HttpResponse(content_type=content_type) |
|
731 | response = HttpResponse(content_type=content_type) | |
728 | response['Content-Disposition'] = 'attachment; filename="%s"' % filename |
|
732 | response['Content-Disposition'] = 'attachment; filename="%s"' % filename | |
729 | response.write(json.dumps(content, indent=2)) |
|
733 | response.write(json.dumps(content, indent=2)) | |
730 |
|
734 | |||
731 | return response |
|
735 | return response | |
732 |
|
736 | |||
733 |
|
737 | |||
734 | @login_required |
|
738 | @login_required | |
735 | def experiment_import(request, id_exp): |
|
739 | def experiment_import(request, id_exp): | |
736 |
|
740 | |||
737 | experiment = get_object_or_404(Experiment, pk=id_exp) |
|
741 | experiment = get_object_or_404(Experiment, pk=id_exp) | |
738 | configurations = Configuration.objects.filter(experiment=experiment) |
|
742 | configurations = Configuration.objects.filter(experiment=experiment) | |
739 |
|
743 | |||
740 | if request.method == 'GET': |
|
744 | if request.method == 'GET': | |
741 | file_form = UploadFileForm() |
|
745 | file_form = UploadFileForm() | |
742 |
|
746 | |||
743 | if request.method == 'POST': |
|
747 | if request.method == 'POST': | |
744 | file_form = UploadFileForm(request.POST, request.FILES) |
|
748 | file_form = UploadFileForm(request.POST, request.FILES) | |
745 |
|
749 | |||
746 | if file_form.is_valid(): |
|
750 | if file_form.is_valid(): | |
747 | new_exp = experiment.dict_to_parms( |
|
751 | new_exp = experiment.dict_to_parms( | |
748 | json.load(request.FILES['file']), CONF_MODELS) |
|
752 | json.load(request.FILES['file']), CONF_MODELS) | |
749 | messages.success( |
|
753 | messages.success( | |
750 | request, "Parameters imported from: '%s'." % request.FILES['file'].name) |
|
754 | request, "Parameters imported from: '%s'." % request.FILES['file'].name) | |
751 | return redirect(new_exp.get_absolute_url_edit()) |
|
755 | return redirect(new_exp.get_absolute_url_edit()) | |
752 |
|
756 | |||
753 | messages.error(request, "Could not import parameters from file") |
|
757 | messages.error(request, "Could not import parameters from file") | |
754 |
|
758 | |||
755 | kwargs = {} |
|
759 | kwargs = {} | |
756 | kwargs['title'] = 'Experiment' |
|
760 | kwargs['title'] = 'Experiment' | |
757 | kwargs['form'] = file_form |
|
761 | kwargs['form'] = file_form | |
758 | kwargs['suptitle'] = 'Importing file' |
|
762 | kwargs['suptitle'] = 'Importing file' | |
759 | kwargs['button'] = 'Import' |
|
763 | kwargs['button'] = 'Import' | |
760 | kwargs['menu_experiments'] = 'active' |
|
764 | kwargs['menu_experiments'] = 'active' | |
761 |
|
765 | |||
762 | kwargs.update(sidebar(experiment=experiment)) |
|
766 | kwargs.update(sidebar(experiment=experiment)) | |
763 |
|
767 | |||
764 | return render(request, 'experiment_import.html', kwargs) |
|
768 | return render(request, 'experiment_import.html', kwargs) | |
765 |
|
769 | |||
766 |
|
770 | |||
767 | @login_required |
|
771 | @login_required | |
768 | def experiment_start(request, id_exp): |
|
772 | def experiment_start(request, id_exp): | |
769 |
|
773 | |||
770 | exp = get_object_or_404(Experiment, pk=id_exp) |
|
774 | exp = get_object_or_404(Experiment, pk=id_exp) | |
771 |
|
775 | |||
772 | if exp.status == 2: |
|
776 | if exp.status == 2: | |
773 | messages.warning(request, 'Experiment {} already runnnig'.format(exp)) |
|
777 | messages.warning(request, 'Experiment {} already runnnig'.format(exp)) | |
774 | else: |
|
778 | else: | |
775 | exp.status = exp.start() |
|
779 | exp.status = exp.start() | |
776 | if exp.status == 0: |
|
780 | if exp.status == 0: | |
777 | messages.error(request, 'Experiment {} not start'.format(exp)) |
|
781 | messages.error(request, 'Experiment {} not start'.format(exp)) | |
778 | if exp.status == 2: |
|
782 | if exp.status == 2: | |
779 | messages.success(request, 'Experiment {} started'.format(exp)) |
|
783 | messages.success(request, 'Experiment {} started'.format(exp)) | |
780 |
|
784 | |||
781 | exp.save() |
|
785 | exp.save() | |
782 |
|
786 | |||
783 | return redirect(exp.get_absolute_url()) |
|
787 | return redirect(exp.get_absolute_url()) | |
784 |
|
788 | |||
785 |
|
789 | |||
786 | @login_required |
|
790 | @login_required | |
787 | def experiment_stop(request, id_exp): |
|
791 | def experiment_stop(request, id_exp): | |
788 |
|
792 | |||
789 | exp = get_object_or_404(Experiment, pk=id_exp) |
|
793 | exp = get_object_or_404(Experiment, pk=id_exp) | |
790 |
|
794 | |||
791 | if exp.status == 2: |
|
795 | if exp.status == 2: | |
792 | exp.status = exp.stop() |
|
796 | exp.status = exp.stop() | |
793 | exp.save() |
|
797 | exp.save() | |
794 | messages.success(request, 'Experiment {} stopped'.format(exp)) |
|
798 | messages.success(request, 'Experiment {} stopped'.format(exp)) | |
795 | else: |
|
799 | else: | |
796 | messages.error(request, 'Experiment {} not running'.format(exp)) |
|
800 | messages.error(request, 'Experiment {} not running'.format(exp)) | |
797 |
|
801 | |||
798 | return redirect(exp.get_absolute_url()) |
|
802 | return redirect(exp.get_absolute_url()) | |
799 |
|
803 | |||
800 |
|
804 | |||
801 | def experiment_status(request, id_exp): |
|
805 | def experiment_status(request, id_exp): | |
802 |
|
806 | |||
803 | exp = get_object_or_404(Experiment, pk=id_exp) |
|
807 | exp = get_object_or_404(Experiment, pk=id_exp) | |
804 |
|
808 | |||
805 | exp.get_status() |
|
809 | exp.get_status() | |
806 |
|
810 | |||
807 | return redirect(exp.get_absolute_url()) |
|
811 | return redirect(exp.get_absolute_url()) | |
808 |
|
812 | |||
809 |
|
813 | |||
810 | @login_required |
|
814 | @login_required | |
811 | def experiment_mix(request, id_exp): |
|
815 | def experiment_mix(request, id_exp): | |
812 |
|
816 | |||
813 | experiment = get_object_or_404(Experiment, pk=id_exp) |
|
817 | experiment = get_object_or_404(Experiment, pk=id_exp) | |
814 | rc_confs = [conf for conf in PedestalConfiguration.objects.filter( |
|
818 | rc_confs = [conf for conf in PedestalConfiguration.objects.filter( | |
815 | experiment=id_exp, |
|
819 | experiment=id_exp, | |
816 | type=0, |
|
820 | type=0, | |
817 | mix=False)] |
|
821 | mix=False)] | |
818 |
|
822 | |||
819 | if len(rc_confs) < 2: |
|
823 | if len(rc_confs) < 2: | |
820 | messages.warning( |
|
824 | messages.warning( | |
821 | request, 'You need at least two RC Configurations to make a mix') |
|
825 | request, 'You need at least two RC Configurations to make a mix') | |
822 | return redirect(experiment.get_absolute_url()) |
|
826 | return redirect(experiment.get_absolute_url()) | |
823 |
|
827 | |||
824 | mix_confs = PedestalConfiguration.objects.filter(experiment=id_exp, mix=True, type=0) |
|
828 | mix_confs = PedestalConfiguration.objects.filter(experiment=id_exp, mix=True, type=0) | |
825 |
|
829 | |||
826 | if mix_confs: |
|
830 | if mix_confs: | |
827 | mix = mix_confs[0] |
|
831 | mix = mix_confs[0] | |
828 | else: |
|
832 | else: | |
829 | mix = PedestalConfiguration(experiment=experiment, |
|
833 | mix = PedestalConfiguration(experiment=experiment, | |
830 | device=rc_confs[0].device, |
|
834 | device=rc_confs[0].device, | |
831 | ipp=rc_confs[0].ipp, |
|
835 | ipp=rc_confs[0].ipp, | |
832 | clock_in=rc_confs[0].clock_in, |
|
836 | clock_in=rc_confs[0].clock_in, | |
833 | clock_divider=rc_confs[0].clock_divider, |
|
837 | clock_divider=rc_confs[0].clock_divider, | |
834 | mix=True, |
|
838 | mix=True, | |
835 | parameters='') |
|
839 | parameters='') | |
836 | mix.save() |
|
840 | mix.save() | |
837 |
|
841 | |||
838 | line_type = RCLineType.objects.get(name='mix') |
|
842 | line_type = RCLineType.objects.get(name='mix') | |
839 | print("VIew obteniendo len getlines") |
|
843 | print("VIew obteniendo len getlines") | |
840 | print(len(rc_confs[0].get_lines())) |
|
844 | print(len(rc_confs[0].get_lines())) | |
841 | for i in range(len(rc_confs[0].get_lines())): |
|
845 | for i in range(len(rc_confs[0].get_lines())): | |
842 | line = RCLine(rc_configuration=mix, line_type=line_type, channel=i) |
|
846 | line = RCLine(rc_configuration=mix, line_type=line_type, channel=i) | |
843 | line.save() |
|
847 | line.save() | |
844 |
|
848 | |||
845 | initial = {'name': mix.name, |
|
849 | initial = {'name': mix.name, | |
846 | 'result': parse_mix_result(mix.parameters), |
|
850 | 'result': parse_mix_result(mix.parameters), | |
847 | 'delay': 0, |
|
851 | 'delay': 0, | |
848 | 'mask': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] |
|
852 | 'mask': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] | |
849 | } |
|
853 | } | |
850 |
|
854 | |||
851 | if request.method == 'GET': |
|
855 | if request.method == 'GET': | |
852 | form = RCMixConfigurationForm(confs=rc_confs, initial=initial) |
|
856 | form = RCMixConfigurationForm(confs=rc_confs, initial=initial) | |
853 |
|
857 | |||
854 | if request.method == 'POST': |
|
858 | if request.method == 'POST': | |
855 | result = mix.parameters |
|
859 | result = mix.parameters | |
856 |
|
860 | |||
857 | if '{}|'.format(request.POST['experiment']) in result: |
|
861 | if '{}|'.format(request.POST['experiment']) in result: | |
858 | messages.error(request, 'Configuration already added') |
|
862 | messages.error(request, 'Configuration already added') | |
859 | else: |
|
863 | else: | |
860 | if 'operation' in request.POST: |
|
864 | if 'operation' in request.POST: | |
861 | operation = MIX_OPERATIONS[request.POST['operation']] |
|
865 | operation = MIX_OPERATIONS[request.POST['operation']] | |
862 | else: |
|
866 | else: | |
863 | operation = ' ' |
|
867 | operation = ' ' | |
864 |
|
868 | |||
865 | mode = MIX_MODES[request.POST['mode']] |
|
869 | mode = MIX_MODES[request.POST['mode']] | |
866 |
|
870 | |||
867 | if result: |
|
871 | if result: | |
868 | result = '{}-{}|{}|{}|{}|{}'.format(mix.parameters, |
|
872 | result = '{}-{}|{}|{}|{}|{}'.format(mix.parameters, | |
869 | request.POST['experiment'], |
|
873 | request.POST['experiment'], | |
870 | mode, |
|
874 | mode, | |
871 | operation, |
|
875 | operation, | |
872 | float( |
|
876 | float( | |
873 | request.POST['delay']), |
|
877 | request.POST['delay']), | |
874 | parse_mask( |
|
878 | parse_mask( | |
875 | request.POST.getlist('mask')) |
|
879 | request.POST.getlist('mask')) | |
876 | ) |
|
880 | ) | |
877 | else: |
|
881 | else: | |
878 | result = '{}|{}|{}|{}|{}'.format(request.POST['experiment'], |
|
882 | result = '{}|{}|{}|{}|{}'.format(request.POST['experiment'], | |
879 | mode, |
|
883 | mode, | |
880 | operation, |
|
884 | operation, | |
881 | float(request.POST['delay']), |
|
885 | float(request.POST['delay']), | |
882 | parse_mask( |
|
886 | parse_mask( | |
883 | request.POST.getlist('mask')) |
|
887 | request.POST.getlist('mask')) | |
884 | ) |
|
888 | ) | |
885 |
|
889 | |||
886 | mix.parameters = result |
|
890 | mix.parameters = result | |
887 | mix.save() |
|
891 | mix.save() | |
888 | mix.update_pulses() |
|
892 | mix.update_pulses() | |
889 |
|
893 | |||
890 | initial['result'] = parse_mix_result(result) |
|
894 | initial['result'] = parse_mix_result(result) | |
891 | initial['name'] = mix.name |
|
895 | initial['name'] = mix.name | |
892 |
|
896 | |||
893 | form = RCMixConfigurationForm(initial=initial, confs=rc_confs) |
|
897 | form = RCMixConfigurationForm(initial=initial, confs=rc_confs) | |
894 |
|
898 | |||
895 | kwargs = { |
|
899 | kwargs = { | |
896 | 'title': 'Experiment', |
|
900 | 'title': 'Experiment', | |
897 | 'suptitle': 'Mix Configurations', |
|
901 | 'suptitle': 'Mix Configurations', | |
898 | 'form': form, |
|
902 | 'form': form, | |
899 | 'extra_button': 'Delete', |
|
903 | 'extra_button': 'Delete', | |
900 | 'button': 'Add', |
|
904 | 'button': 'Add', | |
901 | 'cancel': 'Back', |
|
905 | 'cancel': 'Back', | |
902 | 'previous': experiment.get_absolute_url(), |
|
906 | 'previous': experiment.get_absolute_url(), | |
903 | 'id_exp': id_exp, |
|
907 | 'id_exp': id_exp, | |
904 |
|
908 | |||
905 | } |
|
909 | } | |
906 | kwargs['menu_experiments'] = 'active' |
|
910 | kwargs['menu_experiments'] = 'active' | |
907 |
|
911 | |||
908 | return render(request, 'experiment_mix.html', kwargs) |
|
912 | return render(request, 'experiment_mix.html', kwargs) | |
909 |
|
913 | |||
910 |
|
914 | |||
911 | @login_required |
|
915 | @login_required | |
912 | def experiment_mix_delete(request, id_exp): |
|
916 | def experiment_mix_delete(request, id_exp): | |
913 |
|
917 | |||
914 | conf = PedestalConfiguration.objects.get(experiment=id_exp, mix=True, type=0) |
|
918 | conf = PedestalConfiguration.objects.get(experiment=id_exp, mix=True, type=0) | |
915 | values = conf.parameters.split('-') |
|
919 | values = conf.parameters.split('-') | |
916 | conf.parameters = '-'.join(values[:-1]) |
|
920 | conf.parameters = '-'.join(values[:-1]) | |
917 | conf.save() |
|
921 | conf.save() | |
918 |
|
922 | |||
919 | return redirect('url_mix_experiment', id_exp=id_exp) |
|
923 | return redirect('url_mix_experiment', id_exp=id_exp) | |
920 |
|
924 | |||
921 |
|
925 | |||
922 | def experiment_summary(request, id_exp): |
|
926 | def experiment_summary(request, id_exp): | |
923 |
|
927 | |||
924 | experiment = get_object_or_404(Experiment, pk=id_exp) |
|
928 | experiment = get_object_or_404(Experiment, pk=id_exp) | |
925 | configurations = Configuration.objects.filter( |
|
929 | configurations = Configuration.objects.filter( | |
926 | experiment=experiment, type=0) |
|
930 | experiment=experiment, type=0) | |
927 |
|
931 | |||
928 | kwargs = {} |
|
932 | kwargs = {} | |
929 | kwargs['experiment_keys'] = ['radar_system', |
|
933 | kwargs['experiment_keys'] = ['radar_system', | |
930 | 'name', 'freq', 'start_time', 'end_time'] |
|
934 | 'name', 'freq', 'start_time', 'end_time'] | |
931 | kwargs['experiment'] = experiment |
|
935 | kwargs['experiment'] = experiment | |
932 | kwargs['configurations'] = [] |
|
936 | kwargs['configurations'] = [] | |
933 | kwargs['title'] = 'Experiment Summary' |
|
937 | kwargs['title'] = 'Experiment Summary' | |
934 | kwargs['suptitle'] = 'Details' |
|
938 | kwargs['suptitle'] = 'Details' | |
935 | kwargs['button'] = 'Verify Parameters' |
|
939 | kwargs['button'] = 'Verify Parameters' | |
936 |
|
940 | |||
937 | c_vel = 3.0*(10**8) # m/s |
|
941 | c_vel = 3.0*(10**8) # m/s | |
938 | ope_freq = experiment.freq*(10**6) # 1/s |
|
942 | ope_freq = experiment.freq*(10**6) # 1/s | |
939 | radar_lambda = c_vel/ope_freq # m |
|
943 | radar_lambda = c_vel/ope_freq # m | |
940 | kwargs['radar_lambda'] = radar_lambda |
|
944 | kwargs['radar_lambda'] = radar_lambda | |
941 |
|
945 | |||
942 | ipp = None |
|
946 | ipp = None | |
943 | nsa = 1 |
|
947 | nsa = 1 | |
944 | code_id = 0 |
|
948 | code_id = 0 | |
945 | tx_line = {} |
|
949 | tx_line = {} | |
946 |
|
950 | |||
947 | for configuration in configurations.filter(device__device_type__name = 'pedestal'): |
|
951 | for configuration in configurations.filter(device__device_type__name = 'pedestal'): | |
948 |
|
952 | |||
949 | if configuration.mix: |
|
953 | if configuration.mix: | |
950 | continue |
|
954 | continue | |
951 | conf = {'conf': configuration} |
|
955 | conf = {'conf': configuration} | |
952 | conf['keys'] = [] |
|
956 | conf['keys'] = [] | |
953 | conf['NTxs'] = configuration.ntx |
|
957 | conf['NTxs'] = configuration.ntx | |
954 | conf['keys'].append('NTxs') |
|
958 | conf['keys'].append('NTxs') | |
955 | ipp = configuration.ipp |
|
959 | ipp = configuration.ipp | |
956 | conf['IPP'] = ipp |
|
960 | conf['IPP'] = ipp | |
957 | conf['keys'].append('IPP') |
|
961 | conf['keys'].append('IPP') | |
958 | lines = configuration.get_lines(line_type__name='tx') |
|
962 | lines = configuration.get_lines(line_type__name='tx') | |
959 |
|
963 | |||
960 | for tx_line in lines: |
|
964 | for tx_line in lines: | |
961 | tx_params = json.loads(tx_line.params) |
|
965 | tx_params = json.loads(tx_line.params) | |
962 | conf[tx_line.get_name()] = '{} Km'.format(tx_params['pulse_width']) |
|
966 | conf[tx_line.get_name()] = '{} Km'.format(tx_params['pulse_width']) | |
963 | conf['keys'].append(tx_line.get_name()) |
|
967 | conf['keys'].append(tx_line.get_name()) | |
964 | delays = tx_params['delays'] |
|
968 | delays = tx_params['delays'] | |
965 | if delays not in ('', '0'): |
|
969 | if delays not in ('', '0'): | |
966 | n = len(delays.split(',')) |
|
970 | n = len(delays.split(',')) | |
967 | taus = '{} Taus: {}'.format(n, delays) |
|
971 | taus = '{} Taus: {}'.format(n, delays) | |
968 | else: |
|
972 | else: | |
969 | taus = '-' |
|
973 | taus = '-' | |
970 | conf['Taus ({})'.format(tx_line.get_name())] = taus |
|
974 | conf['Taus ({})'.format(tx_line.get_name())] = taus | |
971 | conf['keys'].append('Taus ({})'.format(tx_line.get_name())) |
|
975 | conf['keys'].append('Taus ({})'.format(tx_line.get_name())) | |
972 | for code_line in configuration.get_lines(line_type__name='codes'): |
|
976 | for code_line in configuration.get_lines(line_type__name='codes'): | |
973 | code_params = json.loads(code_line.params) |
|
977 | code_params = json.loads(code_line.params) | |
974 | code_id = code_params['code'] |
|
978 | code_id = code_params['code'] | |
975 | if tx_line.pk == int(code_params['TX_ref']): |
|
979 | if tx_line.pk == int(code_params['TX_ref']): | |
976 | conf['Code ({})'.format(tx_line.get_name())] = '{}:{}'.format(RCLineCode.objects.get(pk=code_params['code']), |
|
980 | conf['Code ({})'.format(tx_line.get_name())] = '{}:{}'.format(RCLineCode.objects.get(pk=code_params['code']), | |
977 | '-'.join(code_params['codes'])) |
|
981 | '-'.join(code_params['codes'])) | |
978 | conf['keys'].append('Code ({})'.format(tx_line.get_name())) |
|
982 | conf['keys'].append('Code ({})'.format(tx_line.get_name())) | |
979 |
|
983 | |||
980 | for windows_line in configuration.get_lines(line_type__name='windows'): |
|
984 | for windows_line in configuration.get_lines(line_type__name='windows'): | |
981 | win_params = json.loads(windows_line.params) |
|
985 | win_params = json.loads(windows_line.params) | |
982 | if tx_line.pk == int(win_params['TX_ref']): |
|
986 | if tx_line.pk == int(win_params['TX_ref']): | |
983 | windows = '' |
|
987 | windows = '' | |
984 | nsa = win_params['params'][0]['number_of_samples'] |
|
988 | nsa = win_params['params'][0]['number_of_samples'] | |
985 | for i, params in enumerate(win_params['params']): |
|
989 | for i, params in enumerate(win_params['params']): | |
986 | windows += 'W{}: Ho={first_height} km DH={resolution} km NSA={number_of_samples}<br>'.format( |
|
990 | windows += 'W{}: Ho={first_height} km DH={resolution} km NSA={number_of_samples}<br>'.format( | |
987 | i, **params) |
|
991 | i, **params) | |
988 | conf['Window'] = mark_safe(windows) |
|
992 | conf['Window'] = mark_safe(windows) | |
989 | conf['keys'].append('Window') |
|
993 | conf['keys'].append('Window') | |
990 |
|
994 | |||
991 | kwargs['configurations'].append(conf) |
|
995 | kwargs['configurations'].append(conf) | |
992 |
|
996 | |||
993 | for configuration in configurations.filter(device__device_type__name = 'jars'): |
|
997 | for configuration in configurations.filter(device__device_type__name = 'jars'): | |
994 |
|
998 | |||
995 | conf = {'conf': configuration} |
|
999 | conf = {'conf': configuration} | |
996 | conf['keys'] = [] |
|
1000 | conf['keys'] = [] | |
997 | conf['Type of Data'] = EXPERIMENT_TYPE[configuration.exp_type][1] |
|
1001 | conf['Type of Data'] = EXPERIMENT_TYPE[configuration.exp_type][1] | |
998 | conf['keys'].append('Type of Data') |
|
1002 | conf['keys'].append('Type of Data') | |
999 | channels_number = configuration.channels_number |
|
1003 | channels_number = configuration.channels_number | |
1000 | exp_type = configuration.exp_type |
|
1004 | exp_type = configuration.exp_type | |
1001 | fftpoints = configuration.fftpoints |
|
1005 | fftpoints = configuration.fftpoints | |
1002 | filter_parms = json.loads(configuration.filter_parms) |
|
1006 | filter_parms = json.loads(configuration.filter_parms) | |
1003 | spectral_number = configuration.spectral_number |
|
1007 | spectral_number = configuration.spectral_number | |
1004 | acq_profiles = configuration.acq_profiles |
|
1008 | acq_profiles = configuration.acq_profiles | |
1005 | cohe_integr = configuration.cohe_integr |
|
1009 | cohe_integr = configuration.cohe_integr | |
1006 | profiles_block = configuration.profiles_block |
|
1010 | profiles_block = configuration.profiles_block | |
1007 |
|
1011 | |||
1008 | conf['Num of Profiles'] = acq_profiles |
|
1012 | conf['Num of Profiles'] = acq_profiles | |
1009 | conf['keys'].append('Num of Profiles') |
|
1013 | conf['keys'].append('Num of Profiles') | |
1010 |
|
1014 | |||
1011 | conf['Prof per Block'] = profiles_block |
|
1015 | conf['Prof per Block'] = profiles_block | |
1012 | conf['keys'].append('Prof per Block') |
|
1016 | conf['keys'].append('Prof per Block') | |
1013 |
|
1017 | |||
1014 | conf['Blocks per File'] = configuration.raw_data_blocks |
|
1018 | conf['Blocks per File'] = configuration.raw_data_blocks | |
1015 | conf['keys'].append('Blocks per File') |
|
1019 | conf['keys'].append('Blocks per File') | |
1016 |
|
1020 | |||
1017 | if exp_type == 0: # Short |
|
1021 | if exp_type == 0: # Short | |
1018 | bytes_ = 2 |
|
1022 | bytes_ = 2 | |
1019 | b = nsa*2*bytes_*channels_number |
|
1023 | b = nsa*2*bytes_*channels_number | |
1020 | else: # Float |
|
1024 | else: # Float | |
1021 | bytes_ = 4 |
|
1025 | bytes_ = 4 | |
1022 | channels = channels_number + spectral_number |
|
1026 | channels = channels_number + spectral_number | |
1023 | b = nsa*2*bytes_*fftpoints*channels |
|
1027 | b = nsa*2*bytes_*fftpoints*channels | |
1024 |
|
1028 | |||
1025 | codes_num = 7 |
|
1029 | codes_num = 7 | |
1026 | if code_id == 2: |
|
1030 | if code_id == 2: | |
1027 | codes_num = 7 |
|
1031 | codes_num = 7 | |
1028 | elif code_id == 12: |
|
1032 | elif code_id == 12: | |
1029 | codes_num = 15 |
|
1033 | codes_num = 15 | |
1030 |
|
1034 | |||
1031 | #Jars filter values: |
|
1035 | #Jars filter values: | |
1032 |
|
1036 | |||
1033 | clock = float(filter_parms['clock']) |
|
1037 | clock = float(filter_parms['clock']) | |
1034 | filter_2 = int(filter_parms['cic_2']) |
|
1038 | filter_2 = int(filter_parms['cic_2']) | |
1035 | filter_5 = int(filter_parms['cic_5']) |
|
1039 | filter_5 = int(filter_parms['cic_5']) | |
1036 | filter_fir = int(filter_parms['fir']) |
|
1040 | filter_fir = int(filter_parms['fir']) | |
1037 | Fs_MHz = clock/(filter_2*filter_5*filter_fir) |
|
1041 | Fs_MHz = clock/(filter_2*filter_5*filter_fir) | |
1038 |
|
1042 | |||
1039 | #Jars values: |
|
1043 | #Jars values: | |
1040 | if ipp is not None: |
|
1044 | if ipp is not None: | |
1041 | IPP_units = ipp/0.15*Fs_MHz |
|
1045 | IPP_units = ipp/0.15*Fs_MHz | |
1042 | IPP_us = IPP_units / Fs_MHz |
|
1046 | IPP_us = IPP_units / Fs_MHz | |
1043 | IPP_s = IPP_units / (Fs_MHz * (10**6)) |
|
1047 | IPP_s = IPP_units / (Fs_MHz * (10**6)) | |
1044 | Ts = 1/(Fs_MHz*(10**6)) |
|
1048 | Ts = 1/(Fs_MHz*(10**6)) | |
1045 |
|
1049 | |||
1046 | Va = radar_lambda/(4*Ts*cohe_integr) |
|
1050 | Va = radar_lambda/(4*Ts*cohe_integr) | |
1047 | rate_bh = ((nsa-codes_num)*channels_number*2 * |
|
1051 | rate_bh = ((nsa-codes_num)*channels_number*2 * | |
1048 | bytes_/IPP_us)*(36*(10**8)/cohe_integr) |
|
1052 | bytes_/IPP_us)*(36*(10**8)/cohe_integr) | |
1049 | rate_gh = rate_bh/(1024*1024*1024) |
|
1053 | rate_gh = rate_bh/(1024*1024*1024) | |
1050 |
|
1054 | |||
1051 | conf['Time per Block'] = IPP_s * profiles_block * cohe_integr |
|
1055 | conf['Time per Block'] = IPP_s * profiles_block * cohe_integr | |
1052 | conf['keys'].append('Time per Block') |
|
1056 | conf['keys'].append('Time per Block') | |
1053 | conf['Acq time'] = IPP_s * acq_profiles |
|
1057 | conf['Acq time'] = IPP_s * acq_profiles | |
1054 | conf['keys'].append('Acq time') |
|
1058 | conf['keys'].append('Acq time') | |
1055 | conf['Data rate'] = str(rate_gh)+" (GB/h)" |
|
1059 | conf['Data rate'] = str(rate_gh)+" (GB/h)" | |
1056 | conf['keys'].append('Data rate') |
|
1060 | conf['keys'].append('Data rate') | |
1057 | conf['Va (m/s)'] = Va |
|
1061 | conf['Va (m/s)'] = Va | |
1058 | conf['keys'].append('Va (m/s)') |
|
1062 | conf['keys'].append('Va (m/s)') | |
1059 | conf['Vrange (m/s)'] = 3/(2*IPP_s*cohe_integr) |
|
1063 | conf['Vrange (m/s)'] = 3/(2*IPP_s*cohe_integr) | |
1060 | conf['keys'].append('Vrange (m/s)') |
|
1064 | conf['keys'].append('Vrange (m/s)') | |
1061 |
|
1065 | |||
1062 | kwargs['configurations'].append(conf) |
|
1066 | kwargs['configurations'].append(conf) | |
1063 | kwargs['menu_experiments'] = 'active' |
|
1067 | kwargs['menu_experiments'] = 'active' | |
1064 |
|
1068 | |||
1065 | ###### SIDEBAR ###### |
|
1069 | ###### SIDEBAR ###### | |
1066 | kwargs.update(sidebar(experiment=experiment)) |
|
1070 | kwargs.update(sidebar(experiment=experiment)) | |
1067 |
|
1071 | |||
1068 | return render(request, 'experiment_summary.html', kwargs) |
|
1072 | return render(request, 'experiment_summary.html', kwargs) | |
1069 |
|
1073 | |||
1070 |
|
1074 | |||
1071 | @login_required |
|
1075 | @login_required | |
1072 | def experiment_verify(request, id_exp): |
|
1076 | def experiment_verify(request, id_exp): | |
1073 |
|
1077 | |||
1074 | experiment = get_object_or_404(Experiment, pk=id_exp) |
|
1078 | experiment = get_object_or_404(Experiment, pk=id_exp) | |
1075 | experiment_data = experiment.parms_to_dict() |
|
1079 | experiment_data = experiment.parms_to_dict() | |
1076 | configurations = Configuration.objects.filter( |
|
1080 | configurations = Configuration.objects.filter( | |
1077 | experiment=experiment, type=0) |
|
1081 | experiment=experiment, type=0) | |
1078 |
|
1082 | |||
1079 | kwargs = {} |
|
1083 | kwargs = {} | |
1080 |
|
1084 | |||
1081 | kwargs['experiment_keys'] = ['template', |
|
1085 | kwargs['experiment_keys'] = ['template', | |
1082 | 'radar_system', 'name', 'start_time', 'end_time'] |
|
1086 | 'radar_system', 'name', 'start_time', 'end_time'] | |
1083 | kwargs['experiment'] = experiment |
|
1087 | kwargs['experiment'] = experiment | |
1084 |
|
1088 | |||
1085 | kwargs['configuration_keys'] = ['name', 'device__ip_address', |
|
1089 | kwargs['configuration_keys'] = ['name', 'device__ip_address', | |
1086 | 'device__port_address', 'device__status'] |
|
1090 | 'device__port_address', 'device__status'] | |
1087 | kwargs['configurations'] = configurations |
|
1091 | kwargs['configurations'] = configurations | |
1088 | kwargs['experiment_data'] = experiment_data |
|
1092 | kwargs['experiment_data'] = experiment_data | |
1089 |
|
1093 | |||
1090 | kwargs['title'] = 'Verify Experiment' |
|
1094 | kwargs['title'] = 'Verify Experiment' | |
1091 | kwargs['suptitle'] = 'Parameters' |
|
1095 | kwargs['suptitle'] = 'Parameters' | |
1092 |
|
1096 | |||
1093 | kwargs['button'] = 'Update' |
|
1097 | kwargs['button'] = 'Update' | |
1094 |
|
1098 | |||
1095 | jars_conf = False |
|
1099 | jars_conf = False | |
1096 | rc_conf = False |
|
1100 | rc_conf = False | |
1097 | dds_conf = False |
|
1101 | dds_conf = False | |
1098 |
|
1102 | |||
1099 | for configuration in configurations: |
|
1103 | for configuration in configurations: | |
1100 | #-------------------- JARS -----------------------: |
|
1104 | #-------------------- JARS -----------------------: | |
1101 | if configuration.device.device_type.name == 'jars': |
|
1105 | if configuration.device.device_type.name == 'jars': | |
1102 | jars_conf = True |
|
1106 | jars_conf = True | |
1103 | jars = configuration |
|
1107 | jars = configuration | |
1104 | kwargs['jars_conf'] = jars_conf |
|
1108 | kwargs['jars_conf'] = jars_conf | |
1105 | filter_parms = json.loads(jars.filter_parms) |
|
1109 | filter_parms = json.loads(jars.filter_parms) | |
1106 | kwargs['filter_parms'] = filter_parms |
|
1110 | kwargs['filter_parms'] = filter_parms | |
1107 | #--Sampling Frequency |
|
1111 | #--Sampling Frequency | |
1108 | clock = filter_parms['clock'] |
|
1112 | clock = filter_parms['clock'] | |
1109 | filter_2 = filter_parms['cic_2'] |
|
1113 | filter_2 = filter_parms['cic_2'] | |
1110 | filter_5 = filter_parms['cic_5'] |
|
1114 | filter_5 = filter_parms['cic_5'] | |
1111 | filter_fir = filter_parms['fir'] |
|
1115 | filter_fir = filter_parms['fir'] | |
1112 | samp_freq_jars = clock/filter_2/filter_5/filter_fir |
|
1116 | samp_freq_jars = clock/filter_2/filter_5/filter_fir | |
1113 |
|
1117 | |||
1114 | kwargs['samp_freq_jars'] = samp_freq_jars |
|
1118 | kwargs['samp_freq_jars'] = samp_freq_jars | |
1115 | kwargs['jars'] = configuration |
|
1119 | kwargs['jars'] = configuration | |
1116 |
|
1120 | |||
1117 | #--------------------- RC ----------------------: |
|
1121 | #--------------------- RC ----------------------: | |
1118 | if configuration.device.device_type.name == 'pedestal' and not configuration.mix: |
|
1122 | if configuration.device.device_type.name == 'pedestal' and not configuration.mix: | |
1119 | rc_conf = True |
|
1123 | rc_conf = True | |
1120 | rc = configuration |
|
1124 | rc = configuration | |
1121 |
|
1125 | |||
1122 | rc_parms = configuration.parms_to_dict() |
|
1126 | rc_parms = configuration.parms_to_dict() | |
1123 |
|
1127 | |||
1124 | win_lines = rc.get_lines(line_type__name='windows') |
|
1128 | win_lines = rc.get_lines(line_type__name='windows') | |
1125 | if win_lines: |
|
1129 | if win_lines: | |
1126 | dh = json.loads(win_lines[0].params)['params'][0]['resolution'] |
|
1130 | dh = json.loads(win_lines[0].params)['params'][0]['resolution'] | |
1127 | #--Sampling Frequency |
|
1131 | #--Sampling Frequency | |
1128 | samp_freq_rc = 0.15/dh |
|
1132 | samp_freq_rc = 0.15/dh | |
1129 | kwargs['samp_freq_rc'] = samp_freq_rc |
|
1133 | kwargs['samp_freq_rc'] = samp_freq_rc | |
1130 |
|
1134 | |||
1131 | kwargs['rc_conf'] = rc_conf |
|
1135 | kwargs['rc_conf'] = rc_conf | |
1132 | kwargs['rc'] = configuration |
|
1136 | kwargs['rc'] = configuration | |
1133 |
|
1137 | |||
1134 | #-------------------- DDS ----------------------: |
|
1138 | #-------------------- DDS ----------------------: | |
1135 | if configuration.device.device_type.name == 'dds': |
|
1139 | if configuration.device.device_type.name == 'dds': | |
1136 | dds_conf = True |
|
1140 | dds_conf = True | |
1137 | dds = configuration |
|
1141 | dds = configuration | |
1138 | dds_parms = configuration.parms_to_dict() |
|
1142 | dds_parms = configuration.parms_to_dict() | |
1139 |
|
1143 | |||
1140 | kwargs['dds_conf'] = dds_conf |
|
1144 | kwargs['dds_conf'] = dds_conf | |
1141 | kwargs['dds'] = configuration |
|
1145 | kwargs['dds'] = configuration | |
1142 |
|
1146 | |||
1143 | #------------Validation------------: |
|
1147 | #------------Validation------------: | |
1144 | #Clock |
|
1148 | #Clock | |
1145 | if dds_conf and rc_conf and jars_conf: |
|
1149 | if dds_conf and rc_conf and jars_conf: | |
1146 | if float(filter_parms['clock']) != float(rc_parms['configurations']['byId'][str(rc.pk)]['clock_in']) and float(rc_parms['configurations']['byId'][str(rc.pk)]['clock_in']) != float(dds_parms['configurations']['byId'][str(dds.pk)]['clock']): |
|
1150 | if float(filter_parms['clock']) != float(rc_parms['configurations']['byId'][str(rc.pk)]['clock_in']) and float(rc_parms['configurations']['byId'][str(rc.pk)]['clock_in']) != float(dds_parms['configurations']['byId'][str(dds.pk)]['clock']): | |
1147 | messages.warning(request, "Devices don't have the same clock.") |
|
1151 | messages.warning(request, "Devices don't have the same clock.") | |
1148 | elif rc_conf and jars_conf: |
|
1152 | elif rc_conf and jars_conf: | |
1149 | if float(filter_parms['clock']) != float(rc_parms['configurations']['byId'][str(rc.pk)]['clock_in']): |
|
1153 | if float(filter_parms['clock']) != float(rc_parms['configurations']['byId'][str(rc.pk)]['clock_in']): | |
1150 | messages.warning(request, "Devices don't have the same clock.") |
|
1154 | messages.warning(request, "Devices don't have the same clock.") | |
1151 | elif rc_conf and dds_conf: |
|
1155 | elif rc_conf and dds_conf: | |
1152 | if float(rc_parms['configurations']['byId'][str(rc.pk)]['clock_in']) != float(dds_parms['configurations']['byId'][str(dds.pk)]['clock']): |
|
1156 | if float(rc_parms['configurations']['byId'][str(rc.pk)]['clock_in']) != float(dds_parms['configurations']['byId'][str(dds.pk)]['clock']): | |
1153 | messages.warning(request, "Devices don't have the same clock.") |
|
1157 | messages.warning(request, "Devices don't have the same clock.") | |
1154 | if float(samp_freq_rc) != float(dds_parms['configurations']['byId'][str(dds.pk)]['frequencyA']): |
|
1158 | if float(samp_freq_rc) != float(dds_parms['configurations']['byId'][str(dds.pk)]['frequencyA']): | |
1155 | messages.warning( |
|
1159 | messages.warning( | |
1156 | request, "Devices don't have the same Frequency A.") |
|
1160 | request, "Devices don't have the same Frequency A.") | |
1157 |
|
1161 | |||
1158 | #------------POST METHOD------------: |
|
1162 | #------------POST METHOD------------: | |
1159 | if request.method == 'POST': |
|
1163 | if request.method == 'POST': | |
1160 | if request.POST['suggest_clock']: |
|
1164 | if request.POST['suggest_clock']: | |
1161 | try: |
|
1165 | try: | |
1162 | suggest_clock = float(request.POST['suggest_clock']) |
|
1166 | suggest_clock = float(request.POST['suggest_clock']) | |
1163 | except: |
|
1167 | except: | |
1164 | messages.warning(request, "Invalid value in CLOCK IN.") |
|
1168 | messages.warning(request, "Invalid value in CLOCK IN.") | |
1165 | return redirect('url_verify_experiment', id_exp=experiment.id) |
|
1169 | return redirect('url_verify_experiment', id_exp=experiment.id) | |
1166 | else: |
|
1170 | else: | |
1167 | suggest_clock = "" |
|
1171 | suggest_clock = "" | |
1168 | if suggest_clock: |
|
1172 | if suggest_clock: | |
1169 | if rc_conf: |
|
1173 | if rc_conf: | |
1170 | rc.clock_in = suggest_clock |
|
1174 | rc.clock_in = suggest_clock | |
1171 | rc.save() |
|
1175 | rc.save() | |
1172 | if jars_conf: |
|
1176 | if jars_conf: | |
1173 | filter_parms = jars.filter_parms |
|
1177 | filter_parms = jars.filter_parms | |
1174 | filter_parms = ast.literal_eval(filter_parms) |
|
1178 | filter_parms = ast.literal_eval(filter_parms) | |
1175 | filter_parms['clock'] = suggest_clock |
|
1179 | filter_parms['clock'] = suggest_clock | |
1176 | jars.filter_parms = json.dumps(filter_parms) |
|
1180 | jars.filter_parms = json.dumps(filter_parms) | |
1177 | jars.save() |
|
1181 | jars.save() | |
1178 | kwargs['filter_parms'] = filter_parms |
|
1182 | kwargs['filter_parms'] = filter_parms | |
1179 | if dds_conf: |
|
1183 | if dds_conf: | |
1180 | dds.clock = suggest_clock |
|
1184 | dds.clock = suggest_clock | |
1181 | dds.save() |
|
1185 | dds.save() | |
1182 |
|
1186 | |||
1183 | if request.POST['suggest_frequencyA']: |
|
1187 | if request.POST['suggest_frequencyA']: | |
1184 | try: |
|
1188 | try: | |
1185 | suggest_frequencyA = float(request.POST['suggest_frequencyA']) |
|
1189 | suggest_frequencyA = float(request.POST['suggest_frequencyA']) | |
1186 | except: |
|
1190 | except: | |
1187 | messages.warning(request, "Invalid value in FREQUENCY A.") |
|
1191 | messages.warning(request, "Invalid value in FREQUENCY A.") | |
1188 | return redirect('url_verify_experiment', id_exp=experiment.id) |
|
1192 | return redirect('url_verify_experiment', id_exp=experiment.id) | |
1189 | else: |
|
1193 | else: | |
1190 | suggest_frequencyA = "" |
|
1194 | suggest_frequencyA = "" | |
1191 | if suggest_frequencyA: |
|
1195 | if suggest_frequencyA: | |
1192 | if jars_conf: |
|
1196 | if jars_conf: | |
1193 | filter_parms = jars.filter_parms |
|
1197 | filter_parms = jars.filter_parms | |
1194 | filter_parms = ast.literal_eval(filter_parms) |
|
1198 | filter_parms = ast.literal_eval(filter_parms) | |
1195 | filter_parms['fch'] = suggest_frequencyA |
|
1199 | filter_parms['fch'] = suggest_frequencyA | |
1196 | jars.filter_parms = json.dumps(filter_parms) |
|
1200 | jars.filter_parms = json.dumps(filter_parms) | |
1197 | jars.save() |
|
1201 | jars.save() | |
1198 | kwargs['filter_parms'] = filter_parms |
|
1202 | kwargs['filter_parms'] = filter_parms | |
1199 | if dds_conf: |
|
1203 | if dds_conf: | |
1200 | dds.frequencyA_Mhz = request.POST['suggest_frequencyA'] |
|
1204 | dds.frequencyA_Mhz = request.POST['suggest_frequencyA'] | |
1201 | dds.save() |
|
1205 | dds.save() | |
1202 |
|
1206 | |||
1203 | kwargs['menu_experiments'] = 'active' |
|
1207 | kwargs['menu_experiments'] = 'active' | |
1204 | kwargs.update(sidebar(experiment=experiment)) |
|
1208 | kwargs.update(sidebar(experiment=experiment)) | |
1205 | return render(request, 'experiment_verify.html', kwargs) |
|
1209 | return render(request, 'experiment_verify.html', kwargs) | |
1206 |
|
1210 | |||
1207 |
|
1211 | |||
1208 | def parse_mix_result(s): |
|
1212 | def parse_mix_result(s): | |
1209 |
|
1213 | |||
1210 | values = s.split('-') |
|
1214 | values = s.split('-') | |
1211 | html = 'EXP MOD OPE DELAY MASK\r\n' |
|
1215 | html = 'EXP MOD OPE DELAY MASK\r\n' | |
1212 |
|
1216 | |||
1213 | if not values or values[0] in ('', ' '): |
|
1217 | if not values or values[0] in ('', ' '): | |
1214 | return mark_safe(html) |
|
1218 | return mark_safe(html) | |
1215 |
|
1219 | |||
1216 | for i, value in enumerate(values): |
|
1220 | for i, value in enumerate(values): | |
1217 | if not value: |
|
1221 | if not value: | |
1218 | continue |
|
1222 | continue | |
1219 | pk, mode, operation, delay, mask = value.split('|') |
|
1223 | pk, mode, operation, delay, mask = value.split('|') | |
1220 | conf = PedestalConfiguration.objects.get(pk=pk) |
|
1224 | conf = PedestalConfiguration.objects.get(pk=pk) | |
1221 | if i == 0: |
|
1225 | if i == 0: | |
1222 | html += '{:20.18}{:3}{:4}{:9}km{:>6}\r\n'.format( |
|
1226 | html += '{:20.18}{:3}{:4}{:9}km{:>6}\r\n'.format( | |
1223 | conf.name, |
|
1227 | conf.name, | |
1224 | mode, |
|
1228 | mode, | |
1225 | ' ', |
|
1229 | ' ', | |
1226 | delay, |
|
1230 | delay, | |
1227 | mask) |
|
1231 | mask) | |
1228 | else: |
|
1232 | else: | |
1229 | html += '{:20.18}{:3}{:4}{:9}km{:>6}\r\n'.format( |
|
1233 | html += '{:20.18}{:3}{:4}{:9}km{:>6}\r\n'.format( | |
1230 | conf.name, |
|
1234 | conf.name, | |
1231 | mode, |
|
1235 | mode, | |
1232 | operation, |
|
1236 | operation, | |
1233 | delay, |
|
1237 | delay, | |
1234 | mask) |
|
1238 | mask) | |
1235 |
|
1239 | |||
1236 | return mark_safe(html) |
|
1240 | return mark_safe(html) | |
1237 |
|
1241 | |||
1238 |
|
1242 | |||
1239 | def parse_mask(l): |
|
1243 | def parse_mask(l): | |
1240 |
|
1244 | |||
1241 | values = [] |
|
1245 | values = [] | |
1242 |
|
1246 | |||
1243 | for x in range(16): |
|
1247 | for x in range(16): | |
1244 | if '{}'.format(x) in l: |
|
1248 | if '{}'.format(x) in l: | |
1245 | values.append(1) |
|
1249 | values.append(1) | |
1246 | else: |
|
1250 | else: | |
1247 | values.append(0) |
|
1251 | values.append(0) | |
1248 |
|
1252 | |||
1249 | values.reverse() |
|
1253 | values.reverse() | |
1250 |
|
1254 | |||
1251 | return int(''.join([str(x) for x in values]), 2) |
|
1255 | return int(''.join([str(x) for x in values]), 2) | |
1252 |
|
1256 | |||
1253 |
|
1257 | |||
1254 | def dev_confs(request): |
|
1258 | def dev_confs(request): | |
1255 |
|
1259 | |||
1256 | page = request.GET.get('page') |
|
1260 | page = request.GET.get('page') | |
1257 | order = ('-programmed_date', ) |
|
1261 | order = ('-programmed_date', ) | |
1258 | filters = request.GET.copy() |
|
1262 | filters = request.GET.copy() | |
1259 | if 'my configurations' in filters: |
|
1263 | if 'my configurations' in filters: | |
1260 | filters.pop('my configurations', None) |
|
1264 | filters.pop('my configurations', None) | |
1261 | filters['mine'] = request.user.id |
|
1265 | filters['mine'] = request.user.id | |
1262 | kwargs = get_paginator(Configuration, page, order, filters) |
|
1266 | kwargs = get_paginator(Configuration, page, order, filters) | |
1263 | fields = ['tags', 'template', 'historical'] |
|
1267 | fields = ['tags', 'template', 'historical'] | |
1264 | if request.user.is_authenticated: |
|
1268 | if request.user.is_authenticated: | |
1265 | fields.append('my configurations') |
|
1269 | fields.append('my configurations') | |
1266 | form = FilterForm(initial=request.GET, extra_fields=fields) |
|
1270 | form = FilterForm(initial=request.GET, extra_fields=fields) | |
1267 | kwargs['keys'] = ['name', 'device', 'experiment', |
|
1271 | kwargs['keys'] = ['name', 'device', 'experiment', | |
1268 | 'type', 'programmed_date', 'actions'] |
|
1272 | 'type', 'programmed_date', 'actions'] | |
1269 | kwargs['title'] = 'Configuration' |
|
1273 | kwargs['title'] = 'Configuration' | |
1270 | kwargs['suptitle'] = 'List' |
|
1274 | kwargs['suptitle'] = 'List' | |
1271 | kwargs['no_sidebar'] = True |
|
1275 | kwargs['no_sidebar'] = True | |
1272 | kwargs['form'] = form |
|
1276 | kwargs['form'] = form | |
1273 | kwargs['add_url'] = reverse('url_add_dev_conf', args=[0]) |
|
1277 | kwargs['add_url'] = reverse('url_add_dev_conf', args=[0]) | |
1274 | filters = request.GET.copy() |
|
1278 | filters = request.GET.copy() | |
1275 | filters.pop('page', None) |
|
1279 | filters.pop('page', None) | |
1276 | kwargs['q'] = urlencode(filters) |
|
1280 | kwargs['q'] = urlencode(filters) | |
1277 | kwargs['menu_configurations'] = 'active' |
|
1281 | kwargs['menu_configurations'] = 'active' | |
1278 |
|
1282 | |||
1279 | return render(request, 'base_list.html', kwargs) |
|
1283 | return render(request, 'base_list.html', kwargs) | |
1280 |
|
1284 | |||
1281 |
|
1285 | |||
1282 | def dev_conf(request, id_conf): |
|
1286 | def dev_conf(request, id_conf): | |
1283 |
|
1287 | |||
1284 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1288 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1285 |
|
1289 | |||
1286 | return redirect(conf.get_absolute_url()) |
|
1290 | return redirect(conf.get_absolute_url()) | |
1287 |
|
1291 | |||
1288 |
|
1292 | |||
1289 | @login_required |
|
1293 | @login_required | |
1290 | def dev_conf_new(request, id_exp=0, id_dev=0): |
|
1294 | def dev_conf_new(request, id_exp=0, id_dev=0): | |
1291 |
|
1295 | |||
1292 | if not is_developer(request.user): |
|
1296 | if not is_developer(request.user): | |
1293 | messages.error( |
|
1297 | messages.error( | |
1294 | request, 'Developer required, to create new configurations') |
|
1298 | request, 'Developer required, to create new configurations') | |
1295 | return redirect('index') |
|
1299 | return redirect('index') | |
1296 |
|
1300 | |||
1297 | initial = {} |
|
1301 | initial = {} | |
1298 | kwargs = {} |
|
1302 | kwargs = {} | |
1299 |
|
1303 | |||
1300 | if id_exp != 0: |
|
1304 | if id_exp != 0: | |
1301 | initial['experiment'] = id_exp |
|
1305 | initial['experiment'] = id_exp | |
1302 |
|
1306 | |||
1303 | if id_dev != 0: |
|
1307 | if id_dev != 0: | |
1304 | initial['device'] = id_dev |
|
1308 | initial['device'] = id_dev | |
1305 |
|
1309 | |||
1306 | if request.method == 'GET': |
|
1310 | if request.method == 'GET': | |
1307 |
|
1311 | |||
1308 | if id_dev: |
|
1312 | if id_dev: | |
1309 | kwargs['button'] = 'Create' |
|
1313 | kwargs['button'] = 'Create' | |
1310 | device = Device.objects.get(pk=id_dev) |
|
1314 | device = Device.objects.get(pk=id_dev) | |
1311 | DevConfForm = CONF_FORMS[device.device_type.name] |
|
1315 | DevConfForm = CONF_FORMS[device.device_type.name] | |
1312 | initial['name'] = request.GET['name'] |
|
1316 | initial['name'] = request.GET['name'] | |
1313 | form = DevConfForm(initial=initial) |
|
1317 | form = DevConfForm(initial=initial) | |
1314 | else: |
|
1318 | else: | |
1315 | if 'template' in request.GET: |
|
1319 | if 'template' in request.GET: | |
1316 | if request.GET['template'] == '0': |
|
1320 | if request.GET['template'] == '0': | |
1317 | choices = [(conf.pk, '{}'.format(conf)) |
|
1321 | choices = [(conf.pk, '{}'.format(conf)) | |
1318 | for conf in Configuration.objects.filter(template=True)] |
|
1322 | for conf in Configuration.objects.filter(template=True)] | |
1319 | form = NewForm(initial={'create_from': 2}, |
|
1323 | form = NewForm(initial={'create_from': 2}, | |
1320 | template_choices=choices) |
|
1324 | template_choices=choices) | |
1321 | else: |
|
1325 | else: | |
1322 | kwargs['button'] = 'Create' |
|
1326 | kwargs['button'] = 'Create' | |
1323 | conf = Configuration.objects.get( |
|
1327 | conf = Configuration.objects.get( | |
1324 | pk=request.GET['template']) |
|
1328 | pk=request.GET['template']) | |
1325 | id_dev = conf.device.pk |
|
1329 | id_dev = conf.device.pk | |
1326 | DevConfForm = CONF_FORMS[conf.device.device_type.name] |
|
1330 | DevConfForm = CONF_FORMS[conf.device.device_type.name] | |
1327 | form = DevConfForm(instance=conf, |
|
1331 | form = DevConfForm(instance=conf, | |
1328 | initial={'name': '{}_{:%y%m%d}'.format(conf.name, datetime.now()), |
|
1332 | initial={'name': '{}_{:%y%m%d}'.format(conf.name, datetime.now()), | |
1329 | 'template': False, |
|
1333 | 'template': False, | |
1330 | 'experiment': id_exp}) |
|
1334 | 'experiment': id_exp}) | |
1331 | elif 'blank' in request.GET: |
|
1335 | elif 'blank' in request.GET: | |
1332 | kwargs['button'] = 'Create' |
|
1336 | kwargs['button'] = 'Create' | |
1333 | form = ConfigurationForm(initial=initial) |
|
1337 | form = ConfigurationForm(initial=initial) | |
1334 | else: |
|
1338 | else: | |
1335 | form = NewForm() |
|
1339 | form = NewForm() | |
1336 |
|
1340 | |||
1337 | if request.method == 'POST': |
|
1341 | if request.method == 'POST': | |
1338 |
|
1342 | |||
1339 | device = Device.objects.get(pk=request.POST['device']) |
|
1343 | device = Device.objects.get(pk=request.POST['device']) | |
1340 | DevConfForm = CONF_FORMS[device.device_type.name] |
|
1344 | DevConfForm = CONF_FORMS[device.device_type.name] | |
1341 |
|
1345 | |||
1342 | form = DevConfForm(request.POST) |
|
1346 | form = DevConfForm(request.POST) | |
1343 | kwargs['button'] = 'Create' |
|
1347 | kwargs['button'] = 'Create' | |
1344 | if form.is_valid(): |
|
1348 | if form.is_valid(): | |
1345 | conf = form.save(commit=False) |
|
1349 | conf = form.save(commit=False) | |
1346 | conf.author = request.user |
|
1350 | conf.author = request.user | |
1347 | conf.save() |
|
1351 | conf.save() | |
1348 | return redirect('url_dev_conf', id_conf=conf.pk) |
|
1352 | return redirect('url_dev_conf', id_conf=conf.pk) | |
1349 |
|
1353 | |||
1350 | kwargs['id_exp'] = id_exp |
|
1354 | kwargs['id_exp'] = id_exp | |
1351 | kwargs['form'] = form |
|
1355 | kwargs['form'] = form | |
1352 | kwargs['title'] = 'Configuration' |
|
1356 | kwargs['title'] = 'Configuration' | |
1353 | kwargs['suptitle'] = 'New' |
|
1357 | kwargs['suptitle'] = 'New' | |
1354 | kwargs['menu_configurations'] = 'active' |
|
1358 | kwargs['menu_configurations'] = 'active' | |
1355 |
|
1359 | |||
1356 | if id_dev != 0: |
|
1360 | if id_dev != 0: | |
1357 | device = Device.objects.get(pk=id_dev) |
|
1361 | device = Device.objects.get(pk=id_dev) | |
1358 | kwargs['device'] = device.device_type.name |
|
1362 | kwargs['device'] = device.device_type.name | |
1359 | return render(request, 'dev_conf_edit.html', kwargs) |
|
1363 | return render(request, 'dev_conf_edit.html', kwargs) | |
1360 |
|
1364 | |||
1361 |
|
1365 | |||
1362 | @login_required |
|
1366 | @login_required | |
1363 | def dev_conf_edit(request, id_conf): |
|
1367 | def dev_conf_edit(request, id_conf): | |
1364 |
|
1368 | |||
1365 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1369 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1366 |
|
1370 | |||
1367 | DevConfForm = CONF_FORMS[conf.device.device_type.name] |
|
1371 | DevConfForm = CONF_FORMS[conf.device.device_type.name] | |
1368 |
|
1372 | |||
1369 | if request.method == 'GET': |
|
1373 | if request.method == 'GET': | |
1370 | form = DevConfForm(instance=conf) |
|
1374 | form = DevConfForm(instance=conf) | |
1371 |
|
1375 | |||
1372 | if request.method == 'POST': |
|
1376 | if request.method == 'POST': | |
1373 | form = DevConfForm(request.POST, instance=conf) |
|
1377 | form = DevConfForm(request.POST, instance=conf) | |
1374 |
|
1378 | |||
1375 | if form.is_valid(): |
|
1379 | if form.is_valid(): | |
1376 | form.save() |
|
1380 | form.save() | |
1377 | return redirect('url_dev_conf', id_conf=id_conf) |
|
1381 | return redirect('url_dev_conf', id_conf=id_conf) | |
1378 |
|
1382 | |||
1379 | kwargs = {} |
|
1383 | kwargs = {} | |
1380 | kwargs['form'] = form |
|
1384 | kwargs['form'] = form | |
1381 | kwargs['title'] = 'Device Configuration' |
|
1385 | kwargs['title'] = 'Device Configuration' | |
1382 | kwargs['suptitle'] = 'Edit' |
|
1386 | kwargs['suptitle'] = 'Edit' | |
1383 | kwargs['button'] = 'Update' |
|
1387 | kwargs['button'] = 'Update' | |
1384 | kwargs['menu_configurations'] = 'active' |
|
1388 | kwargs['menu_configurations'] = 'active' | |
1385 |
|
1389 | |||
1386 | ###### SIDEBAR ###### |
|
1390 | ###### SIDEBAR ###### | |
1387 | kwargs.update(sidebar(conf=conf)) |
|
1391 | kwargs.update(sidebar(conf=conf)) | |
1388 |
|
1392 | |||
1389 | return render(request, '%s_conf_edit.html' % conf.device.device_type.name, kwargs) |
|
1393 | return render(request, '%s_conf_edit.html' % conf.device.device_type.name, kwargs) | |
1390 |
|
1394 | |||
1391 |
|
1395 | |||
1392 | @login_required |
|
1396 | @login_required | |
1393 | def dev_conf_start(request, id_conf): |
|
1397 | def dev_conf_start(request, id_conf): | |
1394 |
|
1398 | |||
1395 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1399 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1396 |
|
1400 | |||
1397 | if conf.start_device(): |
|
1401 | if conf.start_device(): | |
1398 | messages.success(request, conf.message) |
|
1402 | messages.success(request, conf.message) | |
1399 | else: |
|
1403 | else: | |
1400 | messages.error(request, conf.message) |
|
1404 | messages.error(request, conf.message) | |
1401 |
|
1405 | |||
1402 | #conf.status_device() |
|
1406 | #conf.status_device() | |
1403 |
|
1407 | |||
1404 | return redirect(conf.get_absolute_url()) |
|
1408 | return redirect(conf.get_absolute_url()) | |
1405 |
|
1409 | |||
1406 |
|
1410 | |||
1407 | @login_required |
|
1411 | @login_required | |
1408 | def dev_conf_stop(request, id_conf): |
|
1412 | def dev_conf_stop(request, id_conf): | |
1409 |
|
1413 | |||
1410 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1414 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1411 |
|
1415 | |||
1412 | if conf.stop_device(): |
|
1416 | if conf.stop_device(): | |
1413 | messages.success(request, conf.message) |
|
1417 | messages.success(request, conf.message) | |
1414 | else: |
|
1418 | else: | |
1415 | messages.error(request, conf.message) |
|
1419 | messages.error(request, conf.message) | |
1416 |
|
1420 | |||
1417 | #conf.status_device() |
|
1421 | #conf.status_device() | |
1418 |
|
1422 | |||
1419 | return redirect(conf.get_absolute_url()) |
|
1423 | return redirect(conf.get_absolute_url()) | |
1420 |
|
1424 | |||
1421 |
|
1425 | |||
1422 | @login_required |
|
1426 | @login_required | |
1423 | def dev_conf_status(request, id_conf): |
|
1427 | def dev_conf_status(request, id_conf): | |
1424 |
|
1428 | |||
1425 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1429 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1426 |
|
1430 | |||
1427 | conf_active = Configuration.objects.filter(pk=conf.device.conf_active).first() |
|
1431 | conf_active = Configuration.objects.filter(pk=conf.device.conf_active).first() | |
1428 | if conf_active!=conf: |
|
1432 | if conf_active!=conf: | |
1429 | url = '#' if conf_active is None else conf_active.get_absolute_url() |
|
1433 | url = '#' if conf_active is None else conf_active.get_absolute_url() | |
1430 | label = 'None' if conf_active is None else conf_active.label |
|
1434 | label = 'None' if conf_active is None else conf_active.label | |
1431 | messages.warning( |
|
1435 | messages.warning( | |
1432 | request, |
|
1436 | request, | |
1433 | mark_safe('The current configuration has not been written to device, the active configuration is <a href="{}">{}</a>'.format( |
|
1437 | mark_safe('The current configuration has not been written to device, the active configuration is <a href="{}">{}</a>'.format( | |
1434 | url, |
|
1438 | url, | |
1435 | label |
|
1439 | label | |
1436 | )) |
|
1440 | )) | |
1437 | ) |
|
1441 | ) | |
1438 |
|
1442 | |||
1439 | return redirect(conf.get_absolute_url()) |
|
1443 | return redirect(conf.get_absolute_url()) | |
1440 |
|
1444 | |||
1441 | if conf.status_device(): |
|
1445 | if conf.status_device(): | |
1442 | messages.success(request, conf.message) |
|
1446 | messages.success(request, conf.message) | |
1443 | else: |
|
1447 | else: | |
1444 | messages.error(request, conf.message) |
|
1448 | messages.error(request, conf.message) | |
1445 |
|
1449 | |||
1446 | return redirect(conf.get_absolute_url()) |
|
1450 | return redirect(conf.get_absolute_url()) | |
1447 |
|
1451 | |||
1448 |
|
1452 | |||
1449 | @login_required |
|
1453 | @login_required | |
1450 | def dev_conf_reset(request, id_conf): |
|
1454 | def dev_conf_reset(request, id_conf): | |
1451 |
|
1455 | |||
1452 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1456 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1453 |
|
1457 | |||
1454 | if conf.reset_device(): |
|
1458 | if conf.reset_device(): | |
1455 | messages.success(request, conf.message) |
|
1459 | messages.success(request, conf.message) | |
1456 | else: |
|
1460 | else: | |
1457 | messages.error(request, conf.message) |
|
1461 | messages.error(request, conf.message) | |
1458 |
|
1462 | |||
1459 | return redirect(conf.get_absolute_url()) |
|
1463 | return redirect(conf.get_absolute_url()) | |
1460 |
|
1464 | |||
1461 |
|
1465 | |||
1462 | @login_required |
|
1466 | @login_required | |
1463 | def dev_conf_write(request, id_conf): |
|
1467 | def dev_conf_write(request, id_conf): | |
1464 |
|
1468 | |||
1465 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1469 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1466 |
|
1470 | |||
1467 | if request.method == 'POST': |
|
1471 | if request.method == 'POST': | |
1468 | if conf.write_device(): |
|
1472 | if conf.write_device(): | |
1469 | conf.device.conf_active = conf.pk |
|
1473 | conf.device.conf_active = conf.pk | |
1470 | conf.device.save() |
|
1474 | conf.device.save() | |
1471 | messages.success(request, conf.message) |
|
1475 | messages.success(request, conf.message) | |
1472 | if has_been_modified(conf): |
|
1476 | if has_been_modified(conf): | |
1473 | conf.clone(type=1, template=False) |
|
1477 | conf.clone(type=1, template=False) | |
1474 | else: |
|
1478 | else: | |
1475 | messages.error(request, conf.message) |
|
1479 | messages.error(request, conf.message) | |
1476 |
|
1480 | |||
1477 | return redirect(get_object_or_404(Configuration, pk=id_conf).get_absolute_url()) |
|
1481 | return redirect(get_object_or_404(Configuration, pk=id_conf).get_absolute_url()) | |
1478 |
|
1482 | |||
1479 | kwargs = { |
|
1483 | kwargs = { | |
1480 | 'title': 'Write Configuration', |
|
1484 | 'title': 'Write Configuration', | |
1481 | 'suptitle': conf.label, |
|
1485 | 'suptitle': conf.label, | |
1482 | 'message': 'Are you sure yo want to write this {} configuration?'.format(conf.device), |
|
1486 | 'message': 'Are you sure yo want to write this {} configuration?'.format(conf.device), | |
1483 | 'delete': False |
|
1487 | 'delete': False | |
1484 | } |
|
1488 | } | |
1485 | kwargs['menu_configurations'] = 'active' |
|
1489 | kwargs['menu_configurations'] = 'active' | |
1486 |
|
1490 | |||
1487 | return render(request, 'confirm.html', kwargs) |
|
1491 | return render(request, 'confirm.html', kwargs) | |
1488 |
|
1492 | |||
1489 |
|
1493 | |||
1490 | @login_required |
|
1494 | @login_required | |
1491 | def dev_conf_read(request, id_conf): |
|
1495 | def dev_conf_read(request, id_conf): | |
1492 |
|
1496 | |||
1493 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1497 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1494 |
|
1498 | |||
1495 | DevConfForm = CONF_FORMS[conf.device.device_type.name] |
|
1499 | DevConfForm = CONF_FORMS[conf.device.device_type.name] | |
1496 |
|
1500 | |||
1497 | if request.method == 'GET': |
|
1501 | if request.method == 'GET': | |
1498 |
|
||||
1499 | parms = conf.read_device() |
|
1502 | parms = conf.read_device() | |
1500 | #conf.status_device() |
|
1503 | #conf.status_device() | |
1501 |
|
1504 | |||
1502 | if not parms: |
|
1505 | if not parms: | |
1503 | messages.error(request, conf.message) |
|
1506 | messages.error(request, conf.message) | |
1504 | return redirect(conf.get_absolute_url()) |
|
1507 | return redirect(conf.get_absolute_url()) | |
1505 |
|
1508 | |||
1506 | form = DevConfForm(initial=parms, instance=conf) |
|
1509 | form = DevConfForm(initial=parms, instance=conf) | |
1507 |
|
1510 | |||
1508 | if request.method == 'POST': |
|
1511 | if request.method == 'POST': | |
1509 | form = DevConfForm(request.POST, instance=conf) |
|
1512 | form = DevConfForm(request.POST, instance=conf) | |
1510 |
|
1513 | |||
1511 | if form.is_valid(): |
|
1514 | if form.is_valid(): | |
1512 | form.save() |
|
1515 | form.save() | |
1513 | return redirect(conf.get_absolute_url()) |
|
1516 | return redirect(conf.get_absolute_url()) | |
1514 |
|
1517 | |||
1515 | messages.error(request, "Parameters could not be saved") |
|
1518 | messages.error(request, "Parameters could not be saved") | |
1516 |
|
1519 | |||
1517 | kwargs = {} |
|
1520 | kwargs = {} | |
1518 | kwargs['id_dev'] = conf.id |
|
1521 | kwargs['id_dev'] = conf.id | |
1519 | kwargs['form'] = form |
|
1522 | kwargs['form'] = form | |
1520 | kwargs['title'] = 'Device Configuration' |
|
1523 | kwargs['title'] = 'Device Configuration' | |
1521 | kwargs['suptitle'] = 'Parameters read from device' |
|
1524 | kwargs['suptitle'] = 'Parameters read from device' | |
1522 | kwargs['button'] = 'Save' |
|
1525 | kwargs['button'] = 'Save' | |
1523 |
|
1526 | |||
1524 | ###### SIDEBAR ###### |
|
1527 | ###### SIDEBAR ###### | |
1525 | kwargs.update(sidebar(conf=conf)) |
|
1528 | kwargs.update(sidebar(conf=conf)) | |
1526 |
|
1529 | |||
1527 | return render(request, '%s_conf_edit.html' % conf.device.device_type.name, kwargs) |
|
1530 | return render(request, '%s_conf_edit.html' % conf.device.device_type.name, kwargs) | |
1528 |
|
1531 | |||
1529 |
|
1532 | |||
1530 | @login_required |
|
1533 | @login_required | |
1531 | def dev_conf_import(request, id_conf): |
|
1534 | def dev_conf_import(request, id_conf): | |
1532 |
|
1535 | |||
1533 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1536 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1534 | DevConfForm = CONF_FORMS[conf.device.device_type.name] |
|
1537 | DevConfForm = CONF_FORMS[conf.device.device_type.name] | |
1535 |
|
1538 | |||
1536 | if request.method == 'GET': |
|
1539 | if request.method == 'GET': | |
1537 | file_form = UploadFileForm() |
|
1540 | file_form = UploadFileForm() | |
1538 |
|
1541 | |||
1539 | if request.method == 'POST': |
|
1542 | if request.method == 'POST': | |
1540 | file_form = UploadFileForm(request.POST, request.FILES) |
|
1543 | file_form = UploadFileForm(request.POST, request.FILES) | |
1541 |
|
1544 | |||
1542 | if file_form.is_valid(): |
|
1545 | if file_form.is_valid(): | |
1543 |
|
1546 | |||
1544 | data = conf.import_from_file(request.FILES['file']) |
|
1547 | data = conf.import_from_file(request.FILES['file']) | |
1545 | parms = Params(data=data).get_conf( |
|
1548 | parms = Params(data=data).get_conf( | |
1546 | dtype=conf.device.device_type.name) |
|
1549 | dtype=conf.device.device_type.name) | |
1547 |
|
1550 | |||
1548 | if parms: |
|
1551 | if parms: | |
1549 |
|
1552 | |||
1550 | form = DevConfForm(initial=parms, instance=conf) |
|
1553 | form = DevConfForm(initial=parms, instance=conf) | |
1551 |
|
1554 | |||
1552 | kwargs = {} |
|
1555 | kwargs = {} | |
1553 | kwargs['id_dev'] = conf.id |
|
1556 | kwargs['id_dev'] = conf.id | |
1554 | kwargs['form'] = form |
|
1557 | kwargs['form'] = form | |
1555 | kwargs['title'] = 'Device Configuration' |
|
1558 | kwargs['title'] = 'Device Configuration' | |
1556 | kwargs['suptitle'] = 'Parameters imported' |
|
1559 | kwargs['suptitle'] = 'Parameters imported' | |
1557 | kwargs['button'] = 'Save' |
|
1560 | kwargs['button'] = 'Save' | |
1558 | kwargs['action'] = conf.get_absolute_url_edit() |
|
1561 | kwargs['action'] = conf.get_absolute_url_edit() | |
1559 | kwargs['previous'] = conf.get_absolute_url() |
|
1562 | kwargs['previous'] = conf.get_absolute_url() | |
1560 |
|
1563 | |||
1561 | ###### SIDEBAR ###### |
|
1564 | ###### SIDEBAR ###### | |
1562 | kwargs.update(sidebar(conf=conf)) |
|
1565 | kwargs.update(sidebar(conf=conf)) | |
1563 |
|
1566 | |||
1564 | messages.success( |
|
1567 | messages.success( | |
1565 | request, "Parameters imported from: '%s'." % request.FILES['file'].name) |
|
1568 | request, "Parameters imported from: '%s'." % request.FILES['file'].name) | |
1566 |
|
1569 | |||
1567 | return render(request, '%s_conf_edit.html' % conf.device.device_type.name, kwargs) |
|
1570 | return render(request, '%s_conf_edit.html' % conf.device.device_type.name, kwargs) | |
1568 |
|
1571 | |||
1569 | messages.error(request, "Could not import parameters from file") |
|
1572 | messages.error(request, "Could not import parameters from file") | |
1570 |
|
1573 | |||
1571 | kwargs = {} |
|
1574 | kwargs = {} | |
1572 | kwargs['id_dev'] = conf.id |
|
1575 | kwargs['id_dev'] = conf.id | |
1573 | kwargs['title'] = 'Device Configuration' |
|
1576 | kwargs['title'] = 'Device Configuration' | |
1574 | kwargs['form'] = file_form |
|
1577 | kwargs['form'] = file_form | |
1575 | kwargs['suptitle'] = 'Importing file' |
|
1578 | kwargs['suptitle'] = 'Importing file' | |
1576 | kwargs['button'] = 'Import' |
|
1579 | kwargs['button'] = 'Import' | |
1577 | kwargs['menu_configurations'] = 'active' |
|
1580 | kwargs['menu_configurations'] = 'active' | |
1578 |
|
1581 | |||
1579 | kwargs.update(sidebar(conf=conf)) |
|
1582 | kwargs.update(sidebar(conf=conf)) | |
1580 |
|
1583 | |||
1581 | return render(request, 'dev_conf_import.html', kwargs) |
|
1584 | return render(request, 'dev_conf_import.html', kwargs) | |
1582 |
|
1585 | |||
1583 |
|
1586 | |||
1584 | @login_required |
|
1587 | @login_required | |
1585 | def dev_conf_export(request, id_conf): |
|
1588 | def dev_conf_export(request, id_conf): | |
1586 |
|
1589 | |||
1587 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1590 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1588 |
|
1591 | |||
1589 | if request.method == 'GET': |
|
1592 | if request.method == 'GET': | |
1590 | file_form = DownloadFileForm(conf.device.device_type.name) |
|
1593 | file_form = DownloadFileForm(conf.device.device_type.name) | |
1591 |
|
1594 | |||
1592 | if request.method == 'POST': |
|
1595 | if request.method == 'POST': | |
1593 | file_form = DownloadFileForm( |
|
1596 | file_form = DownloadFileForm( | |
1594 | conf.device.device_type.name, request.POST) |
|
1597 | conf.device.device_type.name, request.POST) | |
1595 |
|
1598 | |||
1596 | if file_form.is_valid(): |
|
1599 | if file_form.is_valid(): | |
1597 | fields = conf.export_to_file( |
|
1600 | fields = conf.export_to_file( | |
1598 | format=file_form.cleaned_data['format']) |
|
1601 | format=file_form.cleaned_data['format']) | |
1599 | if not fields['content']: |
|
1602 | if not fields['content']: | |
1600 | messages.error(request, conf.message) |
|
1603 | messages.error(request, conf.message) | |
1601 | return redirect(conf.get_absolute_url_export()) |
|
1604 | return redirect(conf.get_absolute_url_export()) | |
1602 | response = HttpResponse(content_type=fields['content_type']) |
|
1605 | response = HttpResponse(content_type=fields['content_type']) | |
1603 | response['Content-Disposition'] = 'attachment; filename="%s"' % fields['filename'] |
|
1606 | response['Content-Disposition'] = 'attachment; filename="%s"' % fields['filename'] | |
1604 | response.write(fields['content']) |
|
1607 | response.write(fields['content']) | |
1605 |
|
1608 | |||
1606 | return response |
|
1609 | return response | |
1607 |
|
1610 | |||
1608 | messages.error(request, "Could not export parameters") |
|
1611 | messages.error(request, "Could not export parameters") | |
1609 |
|
1612 | |||
1610 | kwargs = {} |
|
1613 | kwargs = {} | |
1611 | kwargs['id_dev'] = conf.id |
|
1614 | kwargs['id_dev'] = conf.id | |
1612 | kwargs['title'] = 'Device Configuration' |
|
1615 | kwargs['title'] = 'Device Configuration' | |
1613 | kwargs['form'] = file_form |
|
1616 | kwargs['form'] = file_form | |
1614 | kwargs['suptitle'] = 'Exporting file' |
|
1617 | kwargs['suptitle'] = 'Exporting file' | |
1615 | kwargs['button'] = 'Export' |
|
1618 | kwargs['button'] = 'Export' | |
1616 | kwargs['menu_configurations'] = 'active' |
|
1619 | kwargs['menu_configurations'] = 'active' | |
1617 |
|
1620 | |||
1618 | return render(request, 'dev_conf_export.html', kwargs) |
|
1621 | return render(request, 'dev_conf_export.html', kwargs) | |
1619 |
|
1622 | |||
1620 |
|
1623 | |||
1621 | @login_required |
|
1624 | @login_required | |
1622 | def dev_conf_delete(request, id_conf): |
|
1625 | def dev_conf_delete(request, id_conf): | |
1623 |
|
1626 | |||
1624 | conf = get_object_or_404(Configuration, pk=id_conf) |
|
1627 | conf = get_object_or_404(Configuration, pk=id_conf) | |
1625 |
|
1628 | |||
1626 | if request.method == 'POST': |
|
1629 | if request.method == 'POST': | |
1627 | if is_developer(request.user): |
|
1630 | if is_developer(request.user): | |
1628 | conf.delete() |
|
1631 | conf.delete() | |
1629 | return redirect('url_dev_confs') |
|
1632 | return redirect('url_dev_confs') | |
1630 |
|
1633 | |||
1631 | messages.error(request, 'Not enough permission to delete this object') |
|
1634 | messages.error(request, 'Not enough permission to delete this object') | |
1632 | return redirect(conf.get_absolute_url()) |
|
1635 | return redirect(conf.get_absolute_url()) | |
1633 |
|
1636 | |||
1634 | kwargs = { |
|
1637 | kwargs = { | |
1635 | 'title': 'Delete', |
|
1638 | 'title': 'Delete', | |
1636 | 'suptitle': 'Configuration', |
|
1639 | 'suptitle': 'Configuration', | |
1637 | 'object': conf, |
|
1640 | 'object': conf, | |
1638 | 'delete': True |
|
1641 | 'delete': True | |
1639 | } |
|
1642 | } | |
1640 | kwargs['menu_configurations'] = 'active' |
|
1643 | kwargs['menu_configurations'] = 'active' | |
1641 |
|
1644 | |||
1642 | return render(request, 'confirm.html', kwargs) |
|
1645 | return render(request, 'confirm.html', kwargs) | |
1643 |
|
1646 | |||
1644 |
|
1647 | |||
1645 | def sidebar(**kwargs): |
|
1648 | def sidebar(**kwargs): | |
1646 |
|
1649 | |||
1647 | side_data = {} |
|
1650 | side_data = {} | |
1648 |
|
1651 | |||
1649 | conf = kwargs.get('conf', None) |
|
1652 | conf = kwargs.get('conf', None) | |
1650 | experiment = kwargs.get('experiment', None) |
|
1653 | experiment = kwargs.get('experiment', None) | |
1651 |
|
1654 | |||
1652 | if not experiment: |
|
1655 | if not experiment: | |
1653 | experiment = conf.experiment |
|
1656 | experiment = conf.experiment | |
1654 |
|
1657 | |||
1655 | if experiment: |
|
1658 | if experiment: | |
1656 | side_data['experiment'] = experiment |
|
1659 | side_data['experiment'] = experiment | |
1657 | campaign = experiment.campaign_set.all() |
|
1660 | campaign = experiment.campaign_set.all() | |
1658 | if campaign: |
|
1661 | if campaign: | |
1659 | side_data['campaign'] = campaign[0] |
|
1662 | side_data['campaign'] = campaign[0] | |
1660 | experiments = campaign[0].experiments.all().order_by('name') |
|
1663 | experiments = campaign[0].experiments.all().order_by('name') | |
1661 | else: |
|
1664 | else: | |
1662 | experiments = [experiment] |
|
1665 | experiments = [experiment] | |
1663 | configurations = experiment.configuration_set.filter(type=0) |
|
1666 | configurations = experiment.configuration_set.filter(type=0) | |
1664 | side_data['side_experiments'] = experiments |
|
1667 | side_data['side_experiments'] = experiments | |
1665 | side_data['side_configurations'] = configurations.order_by( |
|
1668 | side_data['side_configurations'] = configurations.order_by( | |
1666 | 'device__device_type__name') |
|
1669 | 'device__device_type__name') | |
1667 |
|
1670 | |||
1668 | return side_data |
|
1671 | return side_data | |
1669 |
|
1672 | |||
1670 |
|
1673 | |||
1671 | def get_paginator(model, page, order, filters={}, n=8): |
|
1674 | def get_paginator(model, page, order, filters={}, n=8): | |
1672 |
|
1675 | |||
1673 | kwargs = {} |
|
1676 | kwargs = {} | |
1674 | query = Q() |
|
1677 | query = Q() | |
1675 | if isinstance(filters, QueryDict): |
|
1678 | if isinstance(filters, QueryDict): | |
1676 | filters = filters.dict() |
|
1679 | filters = filters.dict() | |
1677 | [filters.pop(key) for key in list(filters) if filters[key] in ('', ' ')] |
|
1680 | [filters.pop(key) for key in list(filters) if filters[key] in ('', ' ')] | |
1678 | filters.pop('page', None) |
|
1681 | filters.pop('page', None) | |
1679 |
|
1682 | |||
1680 | fields = [f.name for f in model._meta.get_fields()] |
|
1683 | fields = [f.name for f in model._meta.get_fields()] | |
1681 |
|
1684 | |||
1682 | if 'template' in filters: |
|
1685 | if 'template' in filters: | |
1683 | filters['template'] = True |
|
1686 | filters['template'] = True | |
1684 | if 'historical' in filters: |
|
1687 | if 'historical' in filters: | |
1685 | filters.pop('historical') |
|
1688 | filters.pop('historical') | |
1686 | filters['type'] = 1 |
|
1689 | filters['type'] = 1 | |
1687 | elif 'type' in fields: |
|
1690 | elif 'type' in fields: | |
1688 | filters['type'] = 0 |
|
1691 | filters['type'] = 0 | |
1689 | if 'start_date' in filters: |
|
1692 | if 'start_date' in filters: | |
1690 | filters['start_date__gte'] = filters.pop('start_date') |
|
1693 | filters['start_date__gte'] = filters.pop('start_date') | |
1691 | if 'end_date' in filters: |
|
1694 | if 'end_date' in filters: | |
1692 | filters['start_date__lte'] = filters.pop('end_date') |
|
1695 | filters['start_date__lte'] = filters.pop('end_date') | |
1693 | if 'tags' in filters: |
|
1696 | if 'tags' in filters: | |
1694 | tags = filters.pop('tags') |
|
1697 | tags = filters.pop('tags') | |
1695 | if 'tags' in fields: |
|
1698 | if 'tags' in fields: | |
1696 | query = query | Q(tags__icontains=tags) |
|
1699 | query = query | Q(tags__icontains=tags) | |
1697 | if 'label' in fields: |
|
1700 | if 'label' in fields: | |
1698 | query = query | Q(label__icontains=tags) |
|
1701 | query = query | Q(label__icontains=tags) | |
1699 | if 'location' in fields: |
|
1702 | if 'location' in fields: | |
1700 | query = query | Q(location__name__icontains=tags) |
|
1703 | query = query | Q(location__name__icontains=tags) | |
1701 | if 'device' in fields: |
|
1704 | if 'device' in fields: | |
1702 | query = query | Q(device__device_type__name__icontains=tags) |
|
1705 | query = query | Q(device__device_type__name__icontains=tags) | |
1703 | query = query | Q(device__location__name__icontains=tags) |
|
1706 | query = query | Q(device__location__name__icontains=tags) | |
1704 | if 'device_type' in fields: |
|
1707 | if 'device_type' in fields: | |
1705 | query = query | Q(device_type__name__icontains=tags) |
|
1708 | query = query | Q(device_type__name__icontains=tags) | |
1706 |
|
1709 | |||
1707 | if 'mine' in filters: |
|
1710 | if 'mine' in filters: | |
1708 | filters['author_id'] = filters['mine'] |
|
1711 | filters['author_id'] = filters['mine'] | |
1709 | filters.pop('mine') |
|
1712 | filters.pop('mine') | |
1710 | object_list = model.objects.filter(query, **filters).order_by(*order) |
|
1713 | object_list = model.objects.filter(query, **filters).order_by(*order) | |
1711 | paginator = Paginator(object_list, n) |
|
1714 | paginator = Paginator(object_list, n) | |
1712 |
|
1715 | |||
1713 | try: |
|
1716 | try: | |
1714 | objects = paginator.page(page) |
|
1717 | objects = paginator.page(page) | |
1715 | except PageNotAnInteger: |
|
1718 | except PageNotAnInteger: | |
1716 | objects = paginator.page(1) |
|
1719 | objects = paginator.page(1) | |
1717 | except EmptyPage: |
|
1720 | except EmptyPage: | |
1718 | objects = paginator.page(paginator.num_pages) |
|
1721 | objects = paginator.page(paginator.num_pages) | |
1719 |
|
1722 | |||
1720 | kwargs['objects'] = objects |
|
1723 | kwargs['objects'] = objects | |
1721 | kwargs['offset'] = (int(page)-1)*n if page else 0 |
|
1724 | kwargs['offset'] = (int(page)-1)*n if page else 0 | |
1722 |
|
1725 | |||
1723 | return kwargs |
|
1726 | return kwargs | |
1724 |
|
1727 | |||
1725 |
|
1728 | |||
1726 | def operation(request, id_camp=None): |
|
1729 | def operation(request, id_camp=None): | |
1727 |
|
1730 | |||
1728 | kwargs = {} |
|
1731 | kwargs = {} | |
1729 | kwargs['title'] = 'Radars Operation' |
|
1732 | kwargs['title'] = 'Radars Operation' | |
1730 | kwargs['no_sidebar'] = True |
|
1733 | kwargs['no_sidebar'] = True | |
1731 | kwargs['menu_operation'] = 'active' |
|
1734 | kwargs['menu_operation'] = 'active' | |
1732 | campaigns = Campaign.objects.filter(start_date__lte=datetime.now(), |
|
1735 | campaigns = Campaign.objects.filter(start_date__lte=datetime.now(), | |
1733 | end_date__gte=datetime.now()).order_by('-start_date') |
|
1736 | end_date__gte=datetime.now()).order_by('-start_date') | |
1734 |
|
1737 | |||
1735 | if id_camp: |
|
1738 | if id_camp: | |
1736 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
1739 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
1737 | form = OperationForm( |
|
1740 | form = OperationForm( | |
1738 | initial={'campaign': campaign.id}, campaigns=campaigns) |
|
1741 | initial={'campaign': campaign.id}, campaigns=campaigns) | |
1739 | kwargs['campaign'] = campaign |
|
1742 | kwargs['campaign'] = campaign | |
1740 | else: |
|
1743 | else: | |
1741 | # form = OperationForm(campaigns=campaigns) |
|
1744 | # form = OperationForm(campaigns=campaigns) | |
1742 | kwargs['campaigns'] = campaigns |
|
1745 | kwargs['campaigns'] = campaigns | |
1743 | return render(request, 'operation.html', kwargs) |
|
1746 | return render(request, 'operation.html', kwargs) | |
1744 |
|
1747 | |||
1745 | #---Experiment |
|
1748 | #---Experiment | |
1746 | keys = ['id', 'name', 'start_time', 'end_time', 'status'] |
|
1749 | keys = ['id', 'name', 'start_time', 'end_time', 'status'] | |
1747 | kwargs['experiment_keys'] = keys[1:] |
|
1750 | kwargs['experiment_keys'] = keys[1:] | |
1748 | kwargs['experiments'] = experiments |
|
1751 | kwargs['experiments'] = experiments | |
1749 | #---Radar |
|
1752 | #---Radar | |
1750 | kwargs['locations'] = campaign.get_experiments_by_radar() |
|
1753 | kwargs['locations'] = campaign.get_experiments_by_radar() | |
1751 | kwargs['form'] = form |
|
1754 | kwargs['form'] = form | |
1752 |
|
1755 | |||
1753 | return render(request, 'operation.html', kwargs) |
|
1756 | return render(request, 'operation.html', kwargs) | |
1754 |
|
1757 | |||
1755 |
|
1758 | |||
1756 | @login_required |
|
1759 | @login_required | |
1757 | def radar_start(request, id_camp, id_radar): |
|
1760 | def radar_start(request, id_camp, id_radar): | |
1758 |
|
1761 | |||
1759 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
1762 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
1760 | experiments = campaign.get_experiments_by_radar(id_radar)[0]['experiments'] |
|
1763 | experiments = campaign.get_experiments_by_radar(id_radar)[0]['experiments'] | |
1761 | now = datetime.now() |
|
1764 | now = datetime.now() | |
1762 |
|
1765 | |||
1763 | for exp in experiments: |
|
1766 | for exp in experiments: | |
1764 | #app.control.revoke(exp.task) |
|
1767 | #app.control.revoke(exp.task) | |
1765 | print(exp.status) |
|
1768 | print(exp.status) | |
1766 | start = datetime.combine(datetime.now().date(), exp.start_time) |
|
1769 | start = datetime.combine(datetime.now().date(), exp.start_time) | |
1767 | end = datetime.combine(datetime.now().date(), exp.end_time) |
|
1770 | end = datetime.combine(datetime.now().date(), exp.end_time) | |
1768 | print(exp.start_time) |
|
1771 | print(exp.start_time) | |
1769 | print(exp.end_time) |
|
1772 | print(exp.end_time) | |
1770 |
|
1773 | |||
1771 | print(start) |
|
1774 | print(start) | |
1772 | print(end) |
|
1775 | print(end) | |
1773 | print(is_aware(start)) |
|
1776 | print(is_aware(start)) | |
1774 | print(campaign.start_date) |
|
1777 | print(campaign.start_date) | |
1775 | print(campaign.end_date) |
|
1778 | print(campaign.end_date) | |
1776 | print(is_aware(campaign.start_date)) |
|
1779 | print(is_aware(campaign.start_date)) | |
1777 | if end < start: |
|
1780 | if end < start: | |
1778 | end += timedelta(1) |
|
1781 | end += timedelta(1) | |
1779 |
|
1782 | |||
1780 | if exp.status == 2: |
|
1783 | if exp.status == 2: | |
1781 | messages.warning( |
|
1784 | messages.warning( | |
1782 | request, 'Experiment {} already running'.format(exp)) |
|
1785 | request, 'Experiment {} already running'.format(exp)) | |
1783 | continue |
|
1786 | continue | |
1784 |
|
1787 | |||
1785 | if exp.status == 3: |
|
1788 | if exp.status == 3: | |
1786 | messages.warning( |
|
1789 | messages.warning( | |
1787 | request, 'Experiment {} already programmed'.format(exp)) |
|
1790 | request, 'Experiment {} already programmed'.format(exp)) | |
1788 | continue |
|
1791 | continue | |
1789 |
|
1792 | |||
1790 | if start > campaign.end_date or start < campaign.start_date: |
|
1793 | if start > campaign.end_date or start < campaign.start_date: | |
1791 | messages.warning(request, 'Experiment {} out of date'.format(exp)) |
|
1794 | messages.warning(request, 'Experiment {} out of date'.format(exp)) | |
1792 | continue |
|
1795 | continue | |
1793 |
|
1796 | |||
1794 | app.control.revoke(exp.task) |
|
1797 | app.control.revoke(exp.task) | |
1795 | print("Llego luego del revoke") |
|
1798 | print("Llego luego del revoke") | |
1796 | if now > start and now <= end: |
|
1799 | if now > start and now <= end: | |
1797 | print("Caso now >start and <end") |
|
1800 | print("Caso now >start and <end") | |
1798 | task = task_start.delay(exp.id) |
|
1801 | task = task_start.delay(exp.id) | |
1799 | exp.status = task.wait() |
|
1802 | exp.status = task.wait() | |
1800 | if exp.status == 0: |
|
1803 | if exp.status == 0: | |
1801 | messages.error(request, 'Experiment {} not start'.format(exp)) |
|
1804 | messages.error(request, 'Experiment {} not start'.format(exp)) | |
1802 | if exp.status == 2: |
|
1805 | if exp.status == 2: | |
1803 | messages.success(request, 'Experiment {} started'.format(exp)) |
|
1806 | messages.success(request, 'Experiment {} started'.format(exp)) | |
1804 | else: |
|
1807 | else: | |
1805 | print("Caso now < start o >end") |
|
1808 | print("Caso now < start o >end") | |
1806 | task = task_start.apply_async((exp.pk, ), eta=start)#start+timedelta(hours=5)) |
|
1809 | task = task_start.apply_async((exp.pk, ), eta=start)#start+timedelta(hours=5)) | |
1807 | exp.task = task.id |
|
1810 | exp.task = task.id | |
1808 | exp.status = 3 |
|
1811 | exp.status = 3 | |
1809 | messages.success(request, 'Experiment {} programmed to start at {}'.format(exp, start)) |
|
1812 | messages.success(request, 'Experiment {} programmed to start at {}'.format(exp, start)) | |
1810 |
|
1813 | |||
1811 | exp.save() |
|
1814 | exp.save() | |
1812 |
|
1815 | |||
1813 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) |
|
1816 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) | |
1814 |
|
1817 | |||
1815 |
|
1818 | |||
1816 | @login_required |
|
1819 | @login_required | |
1817 | def radar_stop(request, id_camp, id_radar): |
|
1820 | def radar_stop(request, id_camp, id_radar): | |
1818 |
|
1821 | |||
1819 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
1822 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
1820 | experiments = campaign.get_experiments_by_radar(id_radar)[0]['experiments'] |
|
1823 | experiments = campaign.get_experiments_by_radar(id_radar)[0]['experiments'] | |
1821 | print("Ingreso en stop radar_stop") |
|
1824 | print("Ingreso en stop radar_stop") | |
1822 | for exp in experiments: |
|
1825 | for exp in experiments: | |
1823 |
|
1826 | |||
1824 | if exp.task: |
|
1827 | if exp.task: | |
1825 | print("Ingreso antes de revoke stop") |
|
1828 | print("Ingreso antes de revoke stop") | |
1826 | app.control.revoke(exp.task) |
|
1829 | app.control.revoke(exp.task) | |
1827 |
|
1830 | |||
1828 |
|
1831 | |||
1829 | if exp.status == 2: #status 2 es started |
|
1832 | if exp.status == 2: #status 2 es started | |
1830 | print("llama a exp.stop") |
|
1833 | print("llama a exp.stop") | |
1831 | exp.stop() |
|
1834 | exp.stop() | |
1832 | messages.warning(request, 'Experiment {} stopped'.format(exp)) |
|
1835 | messages.warning(request, 'Experiment {} stopped'.format(exp)) | |
1833 | exp.status = 1 |
|
1836 | exp.status = 1 | |
1834 | exp.save() |
|
1837 | exp.save() | |
1835 |
|
1838 | |||
1836 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) |
|
1839 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) | |
1837 |
|
1840 | |||
1838 |
|
1841 | |||
1839 | @login_required |
|
1842 | @login_required | |
1840 | def radar_refresh(request, id_camp, id_radar): |
|
1843 | def radar_refresh(request, id_camp, id_radar): | |
1841 |
|
1844 | |||
1842 | campaign = get_object_or_404(Campaign, pk=id_camp) |
|
1845 | campaign = get_object_or_404(Campaign, pk=id_camp) | |
1843 | experiments = campaign.get_experiments_by_radar(id_radar)[0]['experiments'] |
|
1846 | experiments = campaign.get_experiments_by_radar(id_radar)[0]['experiments'] | |
1844 |
|
1847 | |||
1845 | i = app.control.inspect() |
|
1848 | i = app.control.inspect() | |
1846 | print(i) |
|
1849 | print(i) | |
1847 | print(i.scheduled()) |
|
1850 | print(i.scheduled()) | |
1848 | print(i.scheduled().values()) |
|
1851 | print(i.scheduled().values()) | |
1849 | scheduled = list(i.scheduled().values())[0] |
|
1852 | scheduled = list(i.scheduled().values())[0] | |
1850 | revoked = list(i.revoked().values())[0] |
|
1853 | revoked = list(i.revoked().values())[0] | |
1851 |
|
1854 | |||
1852 | for exp in experiments: |
|
1855 | for exp in experiments: | |
1853 | if exp.task in revoked: |
|
1856 | if exp.task in revoked: | |
1854 | exp.status = 1 |
|
1857 | exp.status = 1 | |
1855 | elif exp.task in [t['request']['id'] for t in scheduled if 'task_stop' in t['request']['name']]: |
|
1858 | elif exp.task in [t['request']['id'] for t in scheduled if 'task_stop' in t['request']['name']]: | |
1856 | exp.status = 2 |
|
1859 | exp.status = 2 | |
1857 | elif exp.task in [t['request']['id'] for t in scheduled if 'task_start' in t['request']['name']]: |
|
1860 | elif exp.task in [t['request']['id'] for t in scheduled if 'task_start' in t['request']['name']]: | |
1858 | exp.status = 3 |
|
1861 | exp.status = 3 | |
1859 | else: |
|
1862 | else: | |
1860 | exp.status = 4 |
|
1863 | exp.status = 4 | |
1861 | exp.save() |
|
1864 | exp.save() | |
1862 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) |
|
1865 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) | |
1863 |
|
1866 | |||
1864 | @login_required |
|
1867 | @login_required | |
1865 | def revoke_tasks(request, id_camp): |
|
1868 | def revoke_tasks(request, id_camp): | |
1866 |
|
1869 | |||
1867 | i = app.control.inspect() |
|
1870 | i = app.control.inspect() | |
1868 | scheduled = list(i.scheduled().values())[0] |
|
1871 | scheduled = list(i.scheduled().values())[0] | |
1869 | revoked = list(i.revoked().values())[0] |
|
1872 | revoked = list(i.revoked().values())[0] | |
1870 |
|
1873 | |||
1871 | for t in scheduled: |
|
1874 | for t in scheduled: | |
1872 | if t['request']['id'] in revoked: |
|
1875 | if t['request']['id'] in revoked: | |
1873 | continue |
|
1876 | continue | |
1874 | app.control.revoke(t['request']['id']) |
|
1877 | app.control.revoke(t['request']['id']) | |
1875 | exp = Experiment.objects.get(pk=eval(str(t['request']['args']))[0]) |
|
1878 | exp = Experiment.objects.get(pk=eval(str(t['request']['args']))[0]) | |
1876 | eta = t['eta'] |
|
1879 | eta = t['eta'] | |
1877 | task = t['request']['name'].split('.')[-1] |
|
1880 | task = t['request']['name'].split('.')[-1] | |
1878 | messages.warning(request, 'Scheduled {} at {} for experiment {} revoked'.format(task, eta, exp.name)) |
|
1881 | messages.warning(request, 'Scheduled {} at {} for experiment {} revoked'.format(task, eta, exp.name)) | |
1879 |
|
1882 | |||
1880 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) |
|
1883 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) | |
1881 |
|
1884 | |||
1882 | @login_required |
|
1885 | @login_required | |
1883 | def show_tasks(request, id_camp): |
|
1886 | def show_tasks(request, id_camp): | |
1884 |
|
1887 | |||
1885 | i = app.control.inspect() |
|
1888 | i = app.control.inspect() | |
1886 | scheduled = list(i.scheduled().values())[0] |
|
1889 | scheduled = list(i.scheduled().values())[0] | |
1887 | revoked = list(i.revoked().values())[0] |
|
1890 | revoked = list(i.revoked().values())[0] | |
1888 |
|
1891 | |||
1889 | for t in scheduled: |
|
1892 | for t in scheduled: | |
1890 | if t['request']['id'] in revoked: |
|
1893 | if t['request']['id'] in revoked: | |
1891 | continue |
|
1894 | continue | |
1892 | exp = Experiment.objects.get(pk=eval(str(t['request']['args']))[0]) |
|
1895 | exp = Experiment.objects.get(pk=eval(str(t['request']['args']))[0]) | |
1893 | eta = t['eta'] |
|
1896 | eta = t['eta'] | |
1894 | task = t['request']['name'].split('.')[-1] |
|
1897 | task = t['request']['name'].split('.')[-1] | |
1895 | messages.success(request, 'Task {} scheduled at {} for experiment {}'.format(task, eta, exp.name)) |
|
1898 | messages.success(request, 'Task {} scheduled at {} for experiment {}'.format(task, eta, exp.name)) | |
1896 |
|
1899 | |||
1897 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) |
|
1900 | return HttpResponseRedirect(reverse('url_operation', args=[id_camp])) | |
1898 |
|
1901 | |||
1899 | def real_time(request): |
|
1902 | def real_time(request): | |
1900 |
|
1903 | |||
1901 | graphic_path = "/home/fiorella/Pictures/catwbeanie.jpg" |
|
1904 | graphic_path = "/home/fiorella/Pictures/catwbeanie.jpg" | |
1902 |
|
1905 | |||
1903 | kwargs = {} |
|
1906 | kwargs = {} | |
1904 | kwargs['title'] = 'CLAIRE' |
|
1907 | kwargs['title'] = 'CLAIRE' | |
1905 | kwargs['suptitle'] = 'Real Time' |
|
1908 | kwargs['suptitle'] = 'Real Time' | |
1906 | kwargs['no_sidebar'] = True |
|
1909 | kwargs['no_sidebar'] = True | |
1907 | kwargs['graphic_path'] = graphic_path |
|
1910 | kwargs['graphic_path'] = graphic_path | |
1908 | kwargs['graphic1_path'] = 'http://www.bluemaize.net/im/girls-accessories/shark-beanie-11.jpg' |
|
1911 | kwargs['graphic1_path'] = 'http://www.bluemaize.net/im/girls-accessories/shark-beanie-11.jpg' | |
1909 |
|
1912 | |||
1910 | return render(request, 'real_time.html', kwargs) |
|
1913 | return render(request, 'real_time.html', kwargs) | |
1911 |
|
1914 | |||
1912 | def theme(request, theme): |
|
1915 | def theme(request, theme): | |
1913 |
|
1916 | |||
1914 | user = request.user |
|
1917 | user = request.user | |
1915 | user.profile.theme = theme |
|
1918 | user.profile.theme = theme | |
1916 | user.save() |
|
1919 | user.save() | |
1917 | return redirect('index') |
|
1920 | return redirect('index') |
@@ -1,291 +1,295 | |||||
1 | import ast |
|
1 | import ast | |
2 | import json |
|
2 | import json | |
3 | import requests |
|
3 | import requests | |
4 | import base64 |
|
4 | import base64 | |
5 | import struct |
|
5 | import struct | |
6 | from struct import pack |
|
6 | from struct import pack | |
7 | import time |
|
7 | import time | |
8 | from django.contrib import messages |
|
8 | from django.contrib import messages | |
9 | from django.db import models |
|
9 | from django.db import models | |
10 | from django.urls import reverse |
|
10 | from django.urls import reverse | |
11 | from django.core.validators import MinValueValidator, MaxValueValidator |
|
11 | from django.core.validators import MinValueValidator, MaxValueValidator | |
12 |
|
12 | |||
13 | from apps.main.models import Configuration |
|
13 | from apps.main.models import Configuration | |
14 |
|
14 | |||
15 | AXIS_VALUE = ( |
|
15 | AXIS_VALUE = ( | |
16 | ('AZI', 'azimuth'), |
|
16 | ('AZI', 'azimuth'), | |
17 | ('ELE', 'elevation') |
|
17 | ('ELE', 'elevation') | |
18 | ) |
|
18 | ) | |
19 |
|
19 | |||
20 | class PedestalConfiguration(Configuration): |
|
20 | class PedestalConfiguration(Configuration): | |
21 |
|
21 | |||
22 | axis = models.CharField( |
|
22 | axis = models.CharField( | |
23 | verbose_name='Axis', |
|
23 | verbose_name='Axis', | |
24 | max_length=3, |
|
24 | max_length=3, | |
25 | choices=AXIS_VALUE, |
|
25 | choices=AXIS_VALUE, | |
26 | null=False, |
|
26 | null=False, | |
27 | blank=False |
|
27 | blank=False | |
28 | ) |
|
28 | ) | |
29 |
|
29 | |||
30 | speed = models.FloatField( |
|
30 | speed = models.FloatField( | |
31 | verbose_name='Speed', |
|
31 | verbose_name='Speed', | |
32 | validators=[MinValueValidator(-20), MaxValueValidator(20)], |
|
32 | validators=[MinValueValidator(-20), MaxValueValidator(20)], | |
33 | blank=False, |
|
33 | blank=False, | |
34 | null=False |
|
34 | null=False | |
35 | ) |
|
35 | ) | |
36 |
|
36 | |||
37 | table = models.CharField( |
|
37 | table = models.CharField( | |
38 | verbose_name="Table", |
|
38 | verbose_name="Table", | |
39 | max_length=100, |
|
39 | max_length=100, | |
40 | default='', |
|
40 | default='', | |
41 | blank=False, |
|
41 | blank=False, | |
42 | null=False |
|
42 | null=False | |
43 | ) |
|
43 | ) | |
44 |
|
44 | |||
45 | class Meta: |
|
45 | class Meta: | |
46 | db_table = 'pedestal_configurations' |
|
46 | db_table = 'pedestal_configurations' | |
47 |
|
47 | |||
48 | def __str__(self): |
|
48 | def __str__(self): | |
49 | return str(self.label) |
|
49 | return str(self.label) | |
50 |
|
50 | |||
51 | def get_absolute_url_plot(self): |
|
51 | def get_absolute_url_plot(self): | |
52 | return reverse('url_plot_pedestal_pulses', args=[str(self.id)]) |
|
52 | return reverse('url_plot_pedestal_pulses', args=[str(self.id)]) | |
53 |
|
53 | |||
54 | def request(self, cmd, method='get', **kwargs): |
|
54 | def request(self, cmd, method='get', **kwargs): | |
55 |
|
55 | |||
56 | req = getattr(requests, method)(self.device.url(cmd), **kwargs) |
|
56 | req = getattr(requests, method)(self.device.url(cmd), **kwargs) | |
57 | payload = req.json() |
|
57 | payload = req.json() | |
58 |
|
58 | |||
59 | return payload |
|
59 | return payload | |
60 |
|
60 | |||
61 | def status_device(self): |
|
61 | def status_device(self): | |
62 |
|
62 | |||
63 | try: |
|
63 | try: | |
64 | self.device.status = 0 |
|
64 | #self.device.status = 0 | |
65 | payload = self.request('status') |
|
65 | #payload = self.request('status') | |
66 | if payload['status']=='enable': |
|
66 | payload = requests.get(self.device.url()) | |
67 | self.device.status = 3 |
|
67 | print(payload) | |
|
68 | if payload: | |||
|
69 | self.device.status = 1 | |||
68 | elif payload['status']=='disable': |
|
70 | elif payload['status']=='disable': | |
69 | self.device.status = 2 |
|
71 | self.device.status = 2 | |
70 | else: |
|
72 | else: | |
71 | self.device.status = 1 |
|
73 | self.device.status = 1 | |
72 | self.device.save() |
|
74 | self.device.save() | |
73 | self.message = 'Pedestal status: {}'.format(payload['status']) |
|
75 | self.message = 'Pedestal status: {}'.format(payload['status']) | |
74 | return False |
|
76 | return False | |
75 | except Exception as e: |
|
77 | except Exception as e: | |
76 | if 'No route to host' not in str(e): |
|
78 | if 'No route to host' not in str(e): | |
77 | self.device.status = 4 |
|
79 | self.device.status = 4 | |
78 | self.device.save() |
|
80 | self.device.save() | |
79 | self.message = 'Pedestal status: {}'.format(str(e)) |
|
81 | self.message = 'Pedestal status: {}'.format(str(e)) | |
80 | return False |
|
82 | return False | |
81 |
|
83 | |||
82 | self.device.save() |
|
84 | self.device.save() | |
83 | return True |
|
85 | return True | |
84 |
|
86 | |||
85 | def reset_device(self): |
|
87 | def reset_device(self): | |
86 |
|
88 | |||
87 | try: |
|
89 | try: | |
88 | payload = self.request('reset', 'post') |
|
90 | payload = self.request('reset', 'post') | |
89 | if payload['reset']=='ok': |
|
91 | if payload['reset']=='ok': | |
90 | self.message = 'Pedestal restarted OK' |
|
92 | self.message = 'Pedestal restarted OK' | |
91 | self.device.status = 2 |
|
93 | self.device.status = 2 | |
92 | self.device.save() |
|
94 | self.device.save() | |
93 | else: |
|
95 | else: | |
94 | self.message = 'Pedestal restart fail' |
|
96 | self.message = 'Pedestal restart fail' | |
95 | self.device.status = 4 |
|
97 | self.device.status = 4 | |
96 | self.device.save() |
|
98 | self.device.save() | |
97 | except Exception as e: |
|
99 | except Exception as e: | |
98 | self.message = 'Pedestal reset: {}'.format(str(e)) |
|
100 | self.message = 'Pedestal reset: {}'.format(str(e)) | |
99 | return False |
|
101 | return False | |
100 |
|
102 | |||
101 | return True |
|
103 | return True | |
102 |
|
104 | |||
103 | def stop_device(self): |
|
105 | def stop_device(self): | |
104 |
|
106 | |||
105 | try: |
|
107 | try: | |
106 | command = self.device.url() + "stop" |
|
108 | command = self.device.url() + "stop" | |
107 | r = requests.get(command) |
|
109 | r = requests.get(command) | |
108 | if r: |
|
110 | if r: | |
109 | self.device.status = 4 |
|
111 | self.device.status = 4 | |
110 | self.device.save() |
|
112 | self.device.save() | |
111 | self.message = 'Pedestal stopped' |
|
113 | self.message = 'Pedestal stopped' | |
112 | else: |
|
114 | else: | |
113 | self.device.status = 4 |
|
115 | self.device.status = 4 | |
114 | self.device.save() |
|
116 | self.device.save() | |
115 | return False |
|
117 | return False | |
116 | except Exception as e: |
|
118 | except Exception as e: | |
117 | if 'No route to host' not in str(e): |
|
119 | if 'No route to host' not in str(e): | |
118 | self.device.status = 4 |
|
120 | self.device.status = 4 | |
119 | else: |
|
121 | else: | |
120 | self.device.status = 0 |
|
122 | self.device.status = 0 | |
121 | self.message = 'Pedestal stop: {}'.format(str(e)) |
|
123 | #self.message = 'Pedestal stop: {}'.format(str(e)) | |
|
124 | self.message = "Pedestal can't start, please check network/device connection or IP address/port configuration" | |||
122 | self.device.save() |
|
125 | self.device.save() | |
123 | return False |
|
126 | return False | |
124 |
|
127 | |||
125 | return True |
|
128 | return True | |
126 |
|
129 | |||
127 | def start_device(self): |
|
130 | def start_device(self): | |
128 | print("Entró al start") |
|
131 | ||
129 | try: |
|
132 | try: | |
130 | pedestal = PedestalConfiguration.objects.get(pk=self) |
|
133 | pedestal = PedestalConfiguration.objects.get(pk=self) | |
131 | print(pedestal) |
|
134 | print(pedestal) | |
132 | pedestal_axis = pedestal.get_axis_display() |
|
135 | pedestal_axis = pedestal.get_axis_display() | |
133 | print(pedestal) |
|
136 | print(pedestal) | |
134 | print(pedestal_axis) |
|
137 | print(pedestal_axis) | |
135 | table = pedestal.table |
|
138 | table = pedestal.table | |
136 | print(table) |
|
139 | print(table) | |
137 | li = list(table.split(", ")) |
|
140 | li = list(table.split(", ")) | |
138 | print(li) |
|
141 | print(li) | |
139 | list_of_floats = [] |
|
142 | list_of_floats = [] | |
140 | for item in li: |
|
143 | for item in li: | |
141 | list_of_floats.append(float(item)) |
|
144 | list_of_floats.append(float(item)) | |
142 | print(list_of_floats) |
|
145 | print(list_of_floats) | |
143 | byte_table = [] |
|
146 | byte_table = [] | |
144 | for x in list_of_floats: |
|
147 | for x in list_of_floats: | |
145 | temp = bytearray(struct.pack("f", x)) |
|
148 | temp = bytearray(struct.pack("f", x)) | |
146 | byte_table.append(temp[3]) |
|
149 | byte_table.append(temp[3]) | |
147 | byte_table.append(temp[2]) |
|
150 | byte_table.append(temp[2]) | |
148 | byte_table.append(temp[1]) |
|
151 | byte_table.append(temp[1]) | |
149 | byte_table.append(temp[0]) |
|
152 | byte_table.append(temp[0]) | |
150 | print(byte_table) |
|
153 | print(byte_table) | |
151 | coded_table = base64.urlsafe_b64encode(bytes(byte_table)) |
|
154 | coded_table = base64.urlsafe_b64encode(bytes(byte_table)) | |
152 | coded_table_ascii = coded_table.decode('ascii') |
|
155 | coded_table_ascii = coded_table.decode('ascii') | |
153 | print(coded_table_ascii) |
|
156 | print(coded_table_ascii) | |
154 | data = {'axis': pedestal_axis, 'speed': pedestal.speed, 'table': coded_table_ascii} |
|
157 | data = {'axis': pedestal_axis, 'speed': pedestal.speed, 'table': coded_table_ascii} | |
155 | print(data) |
|
158 | print(data) | |
156 | json_data = json.dumps(data) |
|
159 | json_data = json.dumps(data) | |
157 | print(json_data) |
|
160 | print(json_data) | |
158 | first_position = table[0] |
|
161 | first_position = table[0] | |
159 |
|
162 | |||
160 | if pedestal.axis=='azimuth': |
|
163 | if pedestal.axis=='azimuth': | |
161 | json_az = json.dumps({"axis": 'azimuth', "position": 0.0}) |
|
164 | json_az = json.dumps({"axis": 'azimuth', "position": 0.0}) | |
162 | json_el = json.dumps({"axis": 'elevation', "position": first_position}) |
|
165 | json_el = json.dumps({"axis": 'elevation', "position": first_position}) | |
163 | else: |
|
166 | else: | |
164 | json_az = json.dumps({"axis": 'azimuth', "position": first_position}) |
|
167 | json_az = json.dumps({"axis": 'azimuth', "position": first_position}) | |
165 | json_el = json.dumps({"axis": 'elevation', "position": 0.0}) |
|
168 | json_el = json.dumps({"axis": 'elevation', "position": 0.0}) | |
166 |
|
169 | |||
167 | base64_table = base64.urlsafe_b64encode(json_data.encode('ascii')) |
|
170 | base64_table = base64.urlsafe_b64encode(json_data.encode('ascii')) | |
168 | base64_az = base64.urlsafe_b64encode(json_az.encode('ascii')) |
|
171 | base64_az = base64.urlsafe_b64encode(json_az.encode('ascii')) | |
169 | base64_el = base64.urlsafe_b64encode(json_el.encode('ascii')) |
|
172 | base64_el = base64.urlsafe_b64encode(json_el.encode('ascii')) | |
170 |
|
173 | |||
171 | table_url = self.device.url() + "table?params=" |
|
174 | table_url = self.device.url() + "table?params=" | |
172 | az_url = self.device.url() + "position?params=" |
|
175 | az_url = self.device.url() + "position?params=" | |
173 | el_url = self.device.url() + "position?params=" |
|
176 | el_url = self.device.url() + "position?params=" | |
174 |
|
177 | |||
175 |
|
178 | |||
176 | complete_url = table_url + base64_table.decode('ascii') |
|
179 | complete_url = table_url + base64_table.decode('ascii') | |
177 |
|
180 | |||
178 | az_url = az_url + base64_az.decode('ascii') |
|
181 | az_url = az_url + base64_az.decode('ascii') | |
179 | el_url = el_url + base64_el.decode('ascii') |
|
182 | el_url = el_url + base64_el.decode('ascii') | |
180 | print(complete_url) |
|
183 | print(complete_url) | |
181 | print(az_url) |
|
184 | print(az_url) | |
182 | print(el_url) |
|
185 | print(el_url) | |
183 | r = requests.get(az_url) |
|
186 | r = requests.get(az_url) | |
184 | r = requests.get(el_url) |
|
187 | r = requests.get(el_url) | |
185 | #time.sleep(10) |
|
188 | #time.sleep(10) | |
186 | r = requests.get(complete_url) |
|
189 | r = requests.get(complete_url) | |
187 | if r: |
|
190 | if r: | |
188 | self.device.status = 3 |
|
191 | self.device.status = 3 | |
189 | self.device.save() |
|
192 | self.device.save() | |
190 | self.message = 'Pedestal configured and started' |
|
193 | self.message = 'Pedestal configured and started' | |
191 | else: |
|
194 | else: | |
192 | return False |
|
195 | return False | |
193 | except Exception as e: |
|
196 | except Exception as e: | |
194 | if 'No route to host' not in str(e): |
|
197 | if 'No route to host' not in str(e): | |
195 | self.device.status = 4 |
|
198 | self.device.status = 4 | |
196 | else: |
|
199 | else: | |
197 | self.device.status = 0 |
|
200 | self.device.status = 0 | |
198 | self.message = 'Pedestal start: {}'.format(str(e)) |
|
201 | #self.message = 'Pedestal start: {}'.format(str(e)) | |
|
202 | self.message = "Pedestal can't start, please check network/device connection or IP address/port configuration" | |||
199 | self.device.save() |
|
203 | self.device.save() | |
200 | return False |
|
204 | return False | |
201 |
|
205 | |||
202 | return True |
|
206 | return True | |
203 |
|
207 | |||
204 | #def write_device(self, raw=False): |
|
208 | #def write_device(self, raw=False): | |
205 |
|
209 | |||
206 | if not raw: |
|
210 | if not raw: | |
207 | clock = RCClock.objects.get(rc_configuration=self) |
|
211 | clock = RCClock.objects.get(rc_configuration=self) | |
208 | print(clock) |
|
212 | print(clock) | |
209 | if clock.mode: |
|
213 | if clock.mode: | |
210 | data = {'default': clock.frequency} |
|
214 | data = {'default': clock.frequency} | |
211 | else: |
|
215 | else: | |
212 | data = {'manual': [clock.multiplier, clock.divisor, clock.reference]} |
|
216 | data = {'manual': [clock.multiplier, clock.divisor, clock.reference]} | |
213 | payload = self.request('setfreq', 'post', data=json.dumps(data)) |
|
217 | payload = self.request('setfreq', 'post', data=json.dumps(data)) | |
214 | print(payload) |
|
218 | print(payload) | |
215 | if payload['command'] != 'ok': |
|
219 | if payload['command'] != 'ok': | |
216 | self.message = 'Pedestal write: {}'.format(payload['command']) |
|
220 | self.message = 'Pedestal write: {}'.format(payload['command']) | |
217 | else: |
|
221 | else: | |
218 | self.message = payload['programming'] |
|
222 | self.message = payload['programming'] | |
219 | if payload['programming'] == 'fail': |
|
223 | if payload['programming'] == 'fail': | |
220 | self.message = 'Pedestal write: error programming CGS chip' |
|
224 | self.message = 'Pedestal write: error programming CGS chip' | |
221 |
|
225 | |||
222 | values = [] |
|
226 | values = [] | |
223 | for pulse, delay in zip(self.get_pulses(), self.get_delays()): |
|
227 | for pulse, delay in zip(self.get_pulses(), self.get_delays()): | |
224 | while delay>65536: |
|
228 | while delay>65536: | |
225 | values.append((pulse, 65535)) |
|
229 | values.append((pulse, 65535)) | |
226 | delay -= 65536 |
|
230 | delay -= 65536 | |
227 | values.append((pulse, delay-1)) |
|
231 | values.append((pulse, delay-1)) | |
228 | data = bytearray() |
|
232 | data = bytearray() | |
229 | #reset |
|
233 | #reset | |
230 | data.extend((128, 0)) |
|
234 | data.extend((128, 0)) | |
231 | #disable |
|
235 | #disable | |
232 | data.extend((129, 0)) |
|
236 | data.extend((129, 0)) | |
233 | #SW switch |
|
237 | #SW switch | |
234 | if self.control_sw: |
|
238 | if self.control_sw: | |
235 | data.extend((130, 2)) |
|
239 | data.extend((130, 2)) | |
236 | else: |
|
240 | else: | |
237 | data.extend((130, 0)) |
|
241 | data.extend((130, 0)) | |
238 | #divider |
|
242 | #divider | |
239 | data.extend((131, self.clock_divider-1)) |
|
243 | data.extend((131, self.clock_divider-1)) | |
240 | #enable writing |
|
244 | #enable writing | |
241 | data.extend((139, 62)) |
|
245 | data.extend((139, 62)) | |
242 |
|
246 | |||
243 | last = 0 |
|
247 | last = 0 | |
244 | for tup in values: |
|
248 | for tup in values: | |
245 | vals = pack('<HH', last^tup[0], tup[1]) |
|
249 | vals = pack('<HH', last^tup[0], tup[1]) | |
246 | last = tup[0] |
|
250 | last = tup[0] | |
247 | data.extend((133, vals[1], 132, vals[0], 133, vals[3], 132, vals[2])) |
|
251 | data.extend((133, vals[1], 132, vals[0], 133, vals[3], 132, vals[2])) | |
248 |
|
252 | |||
249 | #enable |
|
253 | #enable | |
250 | data.extend((129, 1)) |
|
254 | data.extend((129, 1)) | |
251 |
|
255 | |||
252 | if raw: |
|
256 | if raw: | |
253 | return b64encode(data) |
|
257 | return b64encode(data) | |
254 |
|
258 | |||
255 | try: |
|
259 | try: | |
256 | payload = self.request('stop', 'post') |
|
260 | payload = self.request('stop', 'post') | |
257 | payload = self.request('reset', 'post') |
|
261 | payload = self.request('reset', 'post') | |
258 | #payload = self.request('divider', 'post', data={'divider': self.clock_divider-1}) |
|
262 | #payload = self.request('divider', 'post', data={'divider': self.clock_divider-1}) | |
259 | #payload = self.request('write', 'post', data=b64encode(bytearray((139, 62))), timeout=20) |
|
263 | #payload = self.request('write', 'post', data=b64encode(bytearray((139, 62))), timeout=20) | |
260 | n = len(data) |
|
264 | n = len(data) | |
261 | x = 0 |
|
265 | x = 0 | |
262 | #while x < n: |
|
266 | #while x < n: | |
263 | payload = self.request('write', 'post', data=b64encode(data)) |
|
267 | payload = self.request('write', 'post', data=b64encode(data)) | |
264 | # x += 1024 |
|
268 | # x += 1024 | |
265 |
|
269 | |||
266 | if payload['write']=='ok': |
|
270 | if payload['write']=='ok': | |
267 | self.device.status = 3 |
|
271 | self.device.status = 3 | |
268 | self.device.save() |
|
272 | self.device.save() | |
269 | self.message = 'Pedestal configured and started' |
|
273 | self.message = 'Pedestal configured and started' | |
270 | else: |
|
274 | else: | |
271 | self.device.status = 1 |
|
275 | self.device.status = 1 | |
272 | self.device.save() |
|
276 | self.device.save() | |
273 | self.message = 'Pedestal write: {}'.format(payload['write']) |
|
277 | self.message = 'Pedestal write: {}'.format(payload['write']) | |
274 | return False |
|
278 | return False | |
275 |
|
279 | |||
276 | #payload = self.request('start', 'post') |
|
280 | #payload = self.request('start', 'post') | |
277 |
|
281 | |||
278 | except Exception as e: |
|
282 | except Exception as e: | |
279 | if 'No route to host' not in str(e): |
|
283 | if 'No route to host' not in str(e): | |
280 | self.device.status = 4 |
|
284 | self.device.status = 4 | |
281 | else: |
|
285 | else: | |
282 | self.device.status = 0 |
|
286 | self.device.status = 0 | |
283 | self.message = 'Pedestal write: {}'.format(str(e)) |
|
287 | self.message = 'Pedestal write: {}'.format(str(e)) | |
284 | self.device.save() |
|
288 | self.device.save() | |
285 | return False |
|
289 | return False | |
286 |
|
290 | |||
287 | return True |
|
291 | return True | |
288 |
|
292 | |||
289 |
|
293 | |||
290 | def get_absolute_url_import(self): |
|
294 | def get_absolute_url_import(self): | |
291 | return reverse('url_import_pedestal_conf', args=[str(self.id)]) |
|
295 | return reverse('url_import_pedestal_conf', args=[str(self.id)]) |
@@ -1,136 +1,139 | |||||
1 |
|
1 | |||
2 | import json |
|
2 | import json | |
3 |
|
3 | |||
4 | from django.contrib import messages |
|
4 | from django.contrib import messages | |
5 | from django.utils.safestring import mark_safe |
|
5 | from django.utils.safestring import mark_safe | |
6 | from django.shortcuts import render, redirect, get_object_or_404, HttpResponse |
|
6 | from django.shortcuts import render, redirect, get_object_or_404, HttpResponse | |
7 | from django.contrib.auth.decorators import login_required |
|
7 | from django.contrib.auth.decorators import login_required | |
8 |
|
8 | |||
9 | from apps.main.models import Experiment, Device |
|
9 | from apps.main.models import Experiment, Device | |
10 | from apps.main.views import sidebar |
|
10 | from apps.main.views import sidebar | |
11 |
|
11 | |||
12 | from .models import PedestalConfiguration |
|
12 | from .models import PedestalConfiguration | |
13 | from .forms import PedestalConfigurationForm, PedestalImportForm |
|
13 | from .forms import PedestalConfigurationForm, PedestalImportForm | |
14 |
|
14 | |||
15 |
|
15 | |||
16 | def conf(request, conf_id): |
|
16 | def conf(request, conf_id): | |
17 |
|
17 | |||
18 | conf = get_object_or_404(PedestalConfiguration, pk=conf_id) |
|
18 | conf = get_object_or_404(PedestalConfiguration, pk=conf_id) | |
19 |
|
19 | |||
20 | kwargs = {} |
|
20 | kwargs = {} | |
21 | kwargs['dev_conf'] = conf |
|
21 | kwargs['dev_conf'] = conf | |
22 | kwargs['dev_conf_keys'] = ['axis', 'speed', 'table'] |
|
22 | kwargs['dev_conf_keys'] = ['axis', 'speed', 'table'] | |
23 |
|
23 | |||
24 | kwargs['title'] = 'Configuration' |
|
24 | kwargs['title'] = 'Configuration' | |
25 | kwargs['suptitle'] = 'Detail' |
|
25 | kwargs['suptitle'] = 'Detail' | |
26 |
|
26 | |||
27 | kwargs['button'] = 'Edit Configuration' |
|
27 | kwargs['button'] = 'Edit Configuration' | |
|
28 | ||||
|
29 | conf.status_device() | |||
|
30 | ||||
28 | ###### SIDEBAR ###### |
|
31 | ###### SIDEBAR ###### | |
29 | kwargs.update(sidebar(conf=conf)) |
|
32 | kwargs.update(sidebar(conf=conf)) | |
30 |
|
33 | |||
31 | return render(request, 'pedestal_conf.html', kwargs) |
|
34 | return render(request, 'pedestal_conf.html', kwargs) | |
32 |
|
35 | |||
33 | @login_required |
|
36 | @login_required | |
34 | def conf_edit(request, conf_id): |
|
37 | def conf_edit(request, conf_id): | |
35 |
|
38 | |||
36 | conf = get_object_or_404(PedestalConfiguration, pk=conf_id) |
|
39 | conf = get_object_or_404(PedestalConfiguration, pk=conf_id) | |
37 | print(conf) |
|
40 | print(conf) | |
38 | #print("fin de carga de params") |
|
41 | #print("fin de carga de params") | |
39 | if request.method=='GET': |
|
42 | if request.method=='GET': | |
40 | print("GET case") |
|
43 | print("GET case") | |
41 | form = PedestalConfigurationForm(instance=conf) |
|
44 | form = PedestalConfigurationForm(instance=conf) | |
42 | print(form) |
|
45 | print(form) | |
43 |
|
46 | |||
44 | elif request.method=='POST': |
|
47 | elif request.method=='POST': | |
45 | #print("ingreso a post conf edit") |
|
48 | #print("ingreso a post conf edit") | |
46 | line_data = {} |
|
49 | line_data = {} | |
47 | conf_data = {} |
|
50 | conf_data = {} | |
48 | clock_data = {} |
|
51 | clock_data = {} | |
49 | extras = [] |
|
52 | extras = [] | |
50 | print("Inicio impresion POST#####") |
|
53 | print("Inicio impresion POST#####") | |
51 | print(request.POST.items) |
|
54 | print(request.POST.items) | |
52 | print("Fin impresion de POST items#####") |
|
55 | print("Fin impresion de POST items#####") | |
53 | #classified post fields |
|
56 | #classified post fields | |
54 | for label,value in request.POST.items(): |
|
57 | for label,value in request.POST.items(): | |
55 | if label=='csrfmiddlewaretoken': |
|
58 | if label=='csrfmiddlewaretoken': | |
56 | continue |
|
59 | continue | |
57 |
|
60 | |||
58 | if label.count('|')==0: |
|
61 | if label.count('|')==0: | |
59 | if label in ('mode', 'multiplier', 'divisor', 'reference', 'frequency'): |
|
62 | if label in ('mode', 'multiplier', 'divisor', 'reference', 'frequency'): | |
60 | clock_data[label] = value |
|
63 | clock_data[label] = value | |
61 | else: |
|
64 | else: | |
62 | conf_data[label] = value |
|
65 | conf_data[label] = value | |
63 | continue |
|
66 | continue | |
64 |
|
67 | |||
65 | elif label.split('|')[0]!='-1': |
|
68 | elif label.split('|')[0]!='-1': | |
66 | extras.append(label) |
|
69 | extras.append(label) | |
67 | continue |
|
70 | continue | |
68 |
|
71 | |||
69 | #print(label) |
|
72 | #print(label) | |
70 | x, pk, name = label.split('|') |
|
73 | x, pk, name = label.split('|') | |
71 |
|
74 | |||
72 | if name=='codes': |
|
75 | if name=='codes': | |
73 | value = [s for s in value.split('\r\n') if s] |
|
76 | value = [s for s in value.split('\r\n') if s] | |
74 |
|
77 | |||
75 | if pk in line_data: |
|
78 | if pk in line_data: | |
76 | line_data[pk][name] = value |
|
79 | line_data[pk][name] = value | |
77 | else: |
|
80 | else: | |
78 | line_data[pk] = {name:value} |
|
81 | line_data[pk] = {name:value} | |
79 | #print(line_data[pk]) |
|
82 | #print(line_data[pk]) | |
80 | #update conf |
|
83 | #update conf | |
81 |
|
84 | |||
82 | form = PedestalConfigurationForm(conf_data, instance=conf) |
|
85 | form = PedestalConfigurationForm(conf_data, instance=conf) | |
83 |
|
86 | |||
84 | #print(request.POST.items()) |
|
87 | #print(request.POST.items()) | |
85 |
|
88 | |||
86 | if form.is_valid(): |
|
89 | if form.is_valid(): | |
87 | form.save() |
|
90 | form.save() | |
88 |
|
91 | |||
89 | messages.success(request, 'Pedestal configuration successfully updated') |
|
92 | messages.success(request, 'Pedestal configuration successfully updated') | |
90 |
|
93 | |||
91 | return redirect(conf.get_absolute_url()) |
|
94 | return redirect(conf.get_absolute_url()) | |
92 |
|
95 | |||
93 | kwargs = {} |
|
96 | kwargs = {} | |
94 | kwargs['dev_conf'] = conf |
|
97 | kwargs['dev_conf'] = conf | |
95 | kwargs['form'] = form |
|
98 | kwargs['form'] = form | |
96 | kwargs['edit'] = True |
|
99 | kwargs['edit'] = True | |
97 |
|
100 | |||
98 | kwargs['title'] = 'Pedestal Configuration' |
|
101 | kwargs['title'] = 'Pedestal Configuration' | |
99 | kwargs['suptitle'] = 'Edit' |
|
102 | kwargs['suptitle'] = 'Edit' | |
100 | kwargs['button'] = 'Update' |
|
103 | kwargs['button'] = 'Update' | |
101 |
|
104 | |||
102 | print(kwargs) |
|
105 | print(kwargs) | |
103 | print(form) |
|
106 | print(form) | |
104 | return render(request, 'pedestal_conf_edit.html', kwargs) |
|
107 | return render(request, 'pedestal_conf_edit.html', kwargs) | |
105 |
|
108 | |||
106 | def import_file(request, conf_id): |
|
109 | def import_file(request, conf_id): | |
107 |
|
110 | |||
108 | conf = get_object_or_404(PedestalConfiguration, pk=conf_id) |
|
111 | conf = get_object_or_404(PedestalConfiguration, pk=conf_id) | |
109 | if request.method=='POST': |
|
112 | if request.method=='POST': | |
110 | form = PedestalImportForm(request.POST, request.FILES) |
|
113 | form = PedestalImportForm(request.POST, request.FILES) | |
111 | if form.is_valid(): |
|
114 | if form.is_valid(): | |
112 | try: |
|
115 | try: | |
113 | data = conf.import_from_file(request.FILES['file_name']) |
|
116 | data = conf.import_from_file(request.FILES['file_name']) | |
114 | conf.dict_to_parms(data) |
|
117 | conf.dict_to_parms(data) | |
115 | messages.success(request, 'Configuration "%s" loaded succesfully' % request.FILES['file_name']) |
|
118 | messages.success(request, 'Configuration "%s" loaded succesfully' % request.FILES['file_name']) | |
116 | return redirect(conf.get_absolute_url_edit()) |
|
119 | return redirect(conf.get_absolute_url_edit()) | |
117 |
|
120 | |||
118 | except Exception as e: |
|
121 | except Exception as e: | |
119 | messages.error(request, 'Error parsing file: "%s" - %s' % (request.FILES['file_name'], repr(e))) |
|
122 | messages.error(request, 'Error parsing file: "%s" - %s' % (request.FILES['file_name'], repr(e))) | |
120 | else: |
|
123 | else: | |
121 | messages.warning(request, 'Your current configuration will be replaced') |
|
124 | messages.warning(request, 'Your current configuration will be replaced') | |
122 | form = PedestalImportForm() |
|
125 | form = PedestalImportForm() | |
123 |
|
126 | |||
124 | kwargs = {} |
|
127 | kwargs = {} | |
125 | kwargs['form'] = form |
|
128 | kwargs['form'] = form | |
126 | kwargs['title'] = 'Pedestal Configuration' |
|
129 | kwargs['title'] = 'Pedestal Configuration' | |
127 | kwargs['suptitle'] = 'Import file' |
|
130 | kwargs['suptitle'] = 'Import file' | |
128 | kwargs['button'] = 'Upload' |
|
131 | kwargs['button'] = 'Upload' | |
129 | kwargs['previous'] = conf.get_absolute_url() |
|
132 | kwargs['previous'] = conf.get_absolute_url() | |
130 |
|
133 | |||
131 | return render(request, 'pedestal_import.html', kwargs) |
|
134 | return render(request, 'pedestal_import.html', kwargs) | |
132 |
|
135 | |||
133 | def conf_raw(request, conf_id): |
|
136 | def conf_raw(request, conf_id): | |
134 | conf = get_object_or_404(PedestalConfiguration, pk=conf_id) |
|
137 | conf = get_object_or_404(PedestalConfiguration, pk=conf_id) | |
135 | raw = conf.write_device(raw=True) |
|
138 | raw = conf.write_device(raw=True) | |
136 | return HttpResponse(raw, content_type='application/json') No newline at end of file |
|
139 | return HttpResponse(raw, content_type='application/json') |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
General Comments 0
You need to be logged in to leave comments.
Login now