Scape
Scape – набор утилит на JavaScript и PHP (с применением NodeJS), позволяющий запустить на суперкомпьютере кафедры "Теоретическая механика" программы на языке JavaScript, при это программы могут как иметь заранее описанные точки параллелизации (выделены блоки, которые можно распараллелить), так и выполняться последовательно. В последнем случае создаётся серия реализаций, для каждой могут передаваться как случайные, так и заранее определённые начальные условия.
Дополнительно результаты вычислений могут быть автоматически обработаны (усреднены) и отправлены по e-mail (также их можно скачать из клиента). При этом закрытие окна клиента не приводит к остановке вычислений на суперкомпьютере, но задачи можно остановить принудительно (для сохранения данных о запущенных задачах в клиенте используются cookie). Также задача может быть приостановлена или остановлена полностью, если загруженность суперкомпьютера более приоритетными задачами не позволяет проводить вычисления.
Таким образом, scape выполняет следующие задачи:
- производит параллельные вычисления,
- обрабатывает результаты,
- отслеживает нагрузку суперкомпьютера.
Автор: Старобинский Егор.
Описание
В качестве примера доступны для расчётов ряд заранее подготовленных задач. Для запуска одной из них следует нажать кнопку "Выбрать", выбрать задачу по названию, указать количество ядер и начальные параметры.
Для загрузки собственной задачи следует использовать кнопку "Загрузить файл". В течение нескольких минут может происходит обработка файла. Если загруженная программа пройдёт все проверки на соответствие требованиям Scape (в первую очередь по безопасности запускаемого кода), программа появится в списке доступных задач.
Для обновления характерик суперкомпьютера используйте кнопку "Обновить".
Клиент
1 /*
2 ------<====== Prelusive JS ======>------
3 */
4 function setCookie(cookieName, cookieValue){
5 document.cookie = cookieName + "=" + encodeURIComponent(cookieValue) + ";expires=" +
6 (new Date(new Date().getFullYear() + 2,0,0)).toUTCString();
7 }
8 function setCookieWithSpecifiedUTCDate(cookieName, cookieValue, cookieDate){
9 document.cookie = cookieName + "=" + encodeURIComponent(cookieValue) + ";expires=" +
10 cookieDate;
11 }
12 function getCookie(cookieName){
13 var cookieValue = document.cookie.match('(^|;)?' + cookieName + '=([^;]*)(;|$)');
14 return cookieValue ? decodeURIComponent(cookieValue[2]) : '';
15 }
16 function deleteCookie(cookieName){
17 document.cookie = cookieName + "=" + ";expires=Thu Jan 01 1970 00:00:00";
18 }
19 /*
20 ------<====== End ======>------
21 ------<====== Generic JS ======>------
22 */
23 function runScapeClient(){
24 document.getElementById('chooseTaskButton').onclick = function(){
25 document.getElementById('createTask').style.display = 'none';
26 document.getElementById('chooseTask').style.display = 'block';
27 };
28 document.getElementById('uploadFilesButton').onclick = function(){
29 document.getElementById('createTask').style.display = 'none';
30 document.getElementById('uploadFiles').style.display = 'block';
31 };
32 document.getElementById('sendTask').onclick = function(){
33 if(!document.getElementById('tasks').selectedIndex){
34 document.getElementById('creation').className = 'error';
35 return;
36 }
37
38 var id = generateId();
39 var task = document.getElementById('tasks').value;
40 var numberOfRealisations = document.getElementById('numberOfRealisations').valueAsNumber;
41 var parameters = document.getElementById('parameters').value;
42
43 chooseTaskClean();
44
45 var taskElement = document.createElement('div');
46 taskElement.id = 'task' + id;
47 taskElement.className = 'task';
48 taskElement.innerHTML = '<span class="title">' + task + ' [' + numberOfRealisations + ']</span><br>' +
49 'Параметры: ' + (parameters ? parameters : 'по умолчанию') +
50 '<br>Прошло: <span id="timeWindow' + id + '" data-time="' + new Date() + '">-</span><br>' +
51 '<span id="stop' + id + '" class="button">Прервать</span>' +
52 '<span id="reload' + id + '" class="button">Перезапустить</span>' +
53 '<span id="result' + id + '" class="button">Результаты</span>';
54 document.getElementById('workspace')
55 .insertBefore(taskElement, document.getElementById('creation').nextSibling);
56 var taskWindow = document.getElementById('task' + id);
57 setTimeout(function(){
58 taskWindow.style.height = '86px';
59 taskWindow.style.color = '#fff';
60 taskWindow.style.opacity = '1';
61 }, 1);
62 var timer = setInterval(function(){
63 var timeWindow = document.getElementById('timeWindow' + id);
64 var startDate = timeWindow.getAttribute('data-time');
65 var elapsedTime = (new Date() - new Date(startDate)) / 1000;
66 timeWindow.innerText = formatTime(elapsedTime);
67 }, 1000);
68 sendTask(id, task, numberOfRealisations, parameters, timer);
69 };
70 document.getElementById('uploadFiles').onclick = function(){
71 uploadFiles(document.getElementById('files').files);
72 };
73 document.getElementById('chooseTaskCancel').onclick = chooseTaskClean;
74 document.getElementById('uploadFilesCancel').onclick = uploadFilesClean;
75 document.getElementById('updateSpecsButton').onclick = updateSpecs;
76 try{
77 if(document.queryCommandEnabled('copy')){
78 document.getElementById('copyResults').onclick = function(){
79 document.getElementById('results').select();
80 document.execCommand('copy');
81 };
82 document.getElementById('copyResults').style.display = 'inline';
83 }else{
84 document.getElementById('selectResults').onclick = function(){
85 document.getElementById('results').select();
86 };
87 document.getElementById('selectResults').style.display = 'inline';
88 }
89 }catch(e){}
90 document.getElementById('saveResults').onclick = saveResult;
91 document.getElementById('closeResults').onclick = function(){
92 document.getElementById('results').innerText = '';
93 document.getElementById('resultsWindow').style.display = 'none';
94 };
95 document.getElementById('message').onclick = function(e){
96 this.style.display = 'none';
97 this.innerHtml = '';
98 this.className = '';
99 }
100 setInterval(updateSpecs, 60000);
101 updateSpecs();
102 updateTasks();
103 }
104
105 var generateId = (function(){var i = 0; return (function(){return ++i;});})();
106
107 function createCORSInstance(method, url){
108 var r = new XMLHttpRequest;
109 "withCredentials" in r ? r.open(method, url, !0) : "undefined" != typeof XDomainRequest ? (r = new XDomainRequest, r.open(method,url)) : r = null
110 return r;
111 }
112 function updateSpecs(){
113 var r = createCORSInstance('GET', 'http://tm.spbstu.ru:8100/specs/');
114 var elements = document.querySelectorAll('[id^=\'specs_\']');
115 for(var key in elements) elements[key].innerText = '-';
116 r.onerror = function(){
117 var message = document.getElementById('message');
118 message.innerHTML = message.innerHTML + 'Не удалось загрузить характеристики.<br>'
119 message.className = 'error';
120 message.style.display = 'block';
121 }
122 r.onload = function(){
123 var specs = JSON.parse(this.response);
124 for(var key in specs) document.getElementById('specs_' + key).innerText = specs[key];
125 };
126 r.send();
127 }
128 function updateTasks(){
129 var r = createCORSInstance('GET', 'http://tm.spbstu.ru:8100/tasks/');
130 var tasks = document.querySelectorAll('#tasks option[value]');
131 for(var i = 0; i < tasks.length; ++i) tasks[i].remove();
132 r.onerror = function(){
133 var message = document.getElementById('message');
134 message.innerHTML = message.innerHTML + 'Не удалось загрузить список заданий.<br>'
135 message.className = 'error';
136 message.style.display = 'block';
137 }
138 r.onload = function(){
139 var tasks = JSON.parse(this.response);
140 var tasksList = document.getElementById('tasks');
141 for(var i = 0; i < tasks.length; ++i){
142 var task = document.createElement('option');
143 task.value = task.innerText = tasks[i].slice(0, -3);
144 tasksList.appendChild(task);
145 }
146 };
147 r.send();
148 }
149 function sendTask(id, task, numberOfRealisations, parameters, timer){
150 var r = createCORSInstance('GET', 'http://tm.spbstu.ru:8100/' + encodeURIComponent(task) + '/' +
151 numberOfRealisations + '/' + parameters.replace(/, /g, '&'));//fix encode
152 console.log('http://tm.spbstu.ru:8100/' + encodeURIComponent(task) + '/' +
153 numberOfRealisations + '/' + parameters.replace(/, /g, '&'));
154 var taskElement = document.getElementById('task' + id);
155 var stopButton = document.getElementById('stop' + id);
156 var reloadButton = document.getElementById('reload' + id);
157 var resultButton = document.getElementById('result' + id);
158 stopButton.style.display = 'inline';
159 reloadButton.style.display = 'none';
160 resultButton.style.display = 'none';
161 stopButton.onclick = function(){
162 r.abort();
163 clearTimeout(timer);
164 taskElement.className = 'default';
165 stopButton.style.display = 'none';
166 reloadButton.style.display = 'inline';
167 resultButton.style.display = 'none';
168 };
169 reloadButton.onclick = function(){
170 r.send();
171 };
172 r.onerror = function(){
173 clearTimeout(timer);
174 taskElement.className = 'error';
175 stopButton.style.display = 'none';
176 reloadButton.style.display = 'inline';
177 resultButton.style.display = 'none';
178 }
179 r.onload = function(){
180 var result = this.response;
181 console.log(result);
182 clearTimeout(timer);
183 taskElement.className = 'success';
184 stopButton.style.display = 'none';
185 reloadButton.style.display = 'none';
186 resultButton.style.display = 'inline';
187 resultButton.onclick = function(){
188 document.getElementById('results').innerText = result;
189 document.getElementById('resultsWindow').style.display = 'block';
190 };
191 };
192 r.send();
193 }
194 function uploadFiles(files){
195 var r = createCORSInstance('POST', 'http://tm.spbstu.ru:8100/upload/');
196 var f = new FormData();
197 for (var i = 0; i < files.length; ++i){
198 var file = files[i];
199 if(!file.type.match('application/x-javascript')){
200 continue;
201 }
202 f.append('files', file, file.name);
203 }
204 r.onerror = function(){
205 var message = document.getElementById('message');
206 message.innerHTML = message.innerHTML + 'Не удалось загрузить файлы.<br>'
207 message.className = 'error';
208 message.style.display = 'block';
209 }
210 r.onload = function(){
211 console.log(this.response);
212 };
213 r.send(f);
214 }
215 function saveResult(){
216 var r = createCORSInstance('POST', './server/give.php');
217 var f = new FormData();
218 f.append('data', document.getElementById('results').innerText);
219
220 r.onerror = function(){
221 var message = document.getElementById('message');
222 message.innerHTML = message.innerHTML + 'Запрос на сохранение не отправлен.<br>'
223 message.className = 'error';
224 message.style.display = 'block';
225 }
226 r.onload = function(){
227 var response = JSON.parse(this.response);
228 if(response['error']){
229 var message = document.getElementById('message');
230 message.innerHTML = message.innerHTML + response + ' Файл не может быть сохранён.<br>'
231 message.className = 'error';
232 message.style.display = 'block';
233 }else{
234 var url = 'http://ailurus.ru/scape/' + decodeURIComponent(response['link']);
235 console.log(url);
236 window.open(url, '_blank');
237 }
238 };
239 r.send(f);
240 }
241 function chooseTaskClean(){
242 document.getElementById('creation').className = 'default';
243 document.getElementById('createTask').style.display = 'block';
244 document.getElementById('chooseTask').style.display = 'none';
245 document.getElementById('defaultTask').selected = 'selected';
246 document.getElementById('numberOfRealisations').value = 50;
247 document.getElementById('parameters').value = '';
248 }
249 function uploadFilesClean(){
250 document.getElementById('creation').className = 'default';
251 document.getElementById('createTask').style.display = 'block';
252 document.getElementById('uploadFiles').style.display = 'none';
253 document.getElementById('files').value = '';
254 }
255 function formatTime(input){
256 var hoursString = '00';
257 var minutesString = '00';
258 var secondsString = '00';
259 var hours = 0;
260 var minutes = 0;
261 var seconds = 0;
262
263 input = Math.ceil(input);
264 hours = Math.floor(input / (60 * 60));
265 input = input % (60 * 60);
266
267 minutes = Math.floor(input / 60);
268 input = input % 60;
269
270 seconds = input;
271
272 hoursString = (hours >= 10) ? hours.toString() : '0' + hours.toString();
273 minutesString = (minutes >= 10) ? minutes.toString() : '0' + minutes.toString();
274 secondsString = (seconds >= 10) ? seconds.toString() : '0' + seconds.toString();
275
276 return hoursString + ':' + minutesString + ':' + secondsString;
277 }
278 /*
279 ------<====== End ======>------
280 */