Scape

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск

Scape – набор утилит на JavaScript и PHP (с применением NodeJS), позволяющий запустить на суперкомпьютере кафедры "Теоретическая механика" программы на языке JavaScript, при это программы могут как иметь заранее описанные точки параллелизации (выделены блоки, которые можно распараллелить), так и выполняться последовательно. В последнем случае создаётся серия реализаций, для каждой могут передаваться как случайные, так и заранее определённые начальные условия.

Дополнительно результаты вычислений могут быть автоматически обработаны (усреднены) и отправлены по e-mail (также их можно скачать из клиента). При этом закрытие окна клиента не приводит к остановке вычислений на суперкомпьютере, но задачи можно остановить принудительно (для сохранения данных о запущенных задачах в клиенте используются cookie). Также задача может быть приостановлена или остановлена полностью, если загруженность суперкомпьютера более приоритетными задачами не позволяет проводить вычисления.

Таким образом, scape выполняет следующие задачи:

  • производит параллельные вычисления,
  • обрабатывает результаты,
  • отслеживает нагрузку суперкомпьютера.

Автор: Старобинский Егор.

Описание

В качестве примера доступны для расчётов ряд заранее подготовленных задач. Для запуска одной из них следует нажать кнопку "Выбрать", выбрать задачу по названию, указать количество ядер и начальные параметры.

Для загрузки собственной задачи следует использовать кнопку "Загрузить файл". В течение нескольких минут может происходит обработка файла. Если загруженная программа пройдёт все проверки на соответствие требованиям Scape (в первую очередь по безопасности запускаемого кода), программа появится в списке доступных задач.

Для обновления характерик суперкомпьютера используйте кнопку "Обновить".


Клиент

Исходный код клиента [JavaScript]:
  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 */