source: src/main/webapp/newsession.xhtml@ 26

Last change on this file since 26 was 26, checked in by bart, 4 years ago

Fixed memory leak. MOPAC2. removed jcenter build dependencies

File size: 21.1 KB
RevLine 
[26]1<?xml version="1.0" encoding="UTF-8"?>
2<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3<head>
4<title>Profiles and Domains list</title>
5<link rel="stylesheet" type="text/css" href="style.css" />
6</head>
7<body onload="init()">
8 <h1>Session</h1>
9
10 On this page you can configure the settings for running a new session
11 and start the session.
12
13 <br /> Protocol:
14 <select id="selectedprotocol" onchange="selectProtocol()">
15 <option value="SAOP">SAOP ( Stacked Alternating Offers
16 Protocol )</option>
17 <option value="MOPAC">MOPAC (Multiple Offers Partial
18 Consensus)</option>
19 <option value="MOPAC2">MOPAC2 (MOPAC but with values for each vote)</option>
20 <option value="AMOP">AMOP (Alternating Multiple Offers
21 Protocol)</option>
22 <option value="SHAOP">SHAOP (Stacked Human Alternating Offers
23 Protocol)</option>
24 </select>
25
26 <div id="votingevaluator">
27 <br /> Voting Evaluator: <select id="selectedevaluator">
28 <option value="LargestAgreement">Largest Agreement</option>
29 <option value="LargestAgreementsLoop">Largest Agreements and Repeat</option>
30 </select>
31 </div>
32 <div id="votingevaluator2">
33 <br /> Voting Evaluator: <select id="selectedevaluator2">
34 <option value="LargestAgreementWithValue">Largest Agreement with Values</option>
35 </select>
36 </div>
37
38
39 <br /> Deadline:
40 <input type="number" id="deadlinevalue" name="deadline" min="1"
41 max="10000" value="10" />
42 <select id="deadlinetype">
43 <option value="ROUNDS">rounds</option>
44 <option value="TIME">seconds</option>
45 </select>
46
47 <br /> Domain/Profile Server:
48 <input type="url" name="url" id="profilesserverurl"
49 value="localhost:8080/profilesserver-1.5.9"
50 pattern=".*:[0-9]+/profilesserver.*" size="30"
51 onchange="connectDomain()"> </input>
52 <br /> Domain:
53 <select id="domainselection" onchange="selectDomain()">
54 <!-- <option>Waiting for profiles server</option> -->
55 </select>
56
57 <br />
58 <br />
59
60 <div id="box" class="box">
61 <br /> <b>Participants</b> <br /> Parties Server: <input type="url"
62 name="url" id="partiesserverurl"
63 value="localhost:8080/partiesserver-1.5.9"
64 pattern=".*:[0-9]+/partiesserver.*" size="30"
65 onchange="connectParties()"> </input> <br /> <br /> <b>Party
66 settings</b> <br /> Party : <select id="partyselection">
67 </select> <br /> Profile: <select id="profileselection"></select> Filter: <input
68 type="text" id="filter" value="" maxlength="40" /> <br />
69 Parameters: {
70 <textarea id="parameters" rows="2" cols="70"
71 onchange="updateParameters()" value="" />
72 } <br /> <br />
73
74 <div id="cobsetting">
75 <input type="checkbox" id="advancedCobSettings"
76 onchange="advancedCobSet()"></input> Advanced COB settings<br />
77 <div id="advancedsettings" style="display: none">
78 <b>COB party settings</b> <br /> Party : <select
79 id="cobpartyselection">
80 </select> <br /> Profile: <select id="cobprofileselection"></select> Filter:
81 <input type="text" id="cobfilter" value="" maxlength="40" /> <br />
82 <!-- -->
83 Parameters: {
84 <textarea id="cobparameters" rows="2" cols="70"
85 onchange="updateCobParameters()" value="" />
86 } <br /> <br />
87 </div>
88 </div>
89
90 <button onclick="addParty()">Add</button>
91
92
93 <br /> <br /> <b>Selected Profiles, Parties for the session</b>
94 <table id="selectedpartiestable" width="100%">
95 <colgroup>
96 <col />
97 <col />
98 <col />
99 <col />
100 <col />
101 <col />
102 </colgroup>
103 <thead>
104 <tr>
105 <th align="center">Party</th>
106 <th align="center">Parameters</th>
107 <th align="center">Profile</th>
108
109 <th align="center">COB Party</th>
110 <th align="center">COB Parameters</th>
111 <th align="center">COB Profile</th>
112 </tr>
113 </thead>
114 <tbody id="partiesList">
115 </tbody>
116 </table>
117
118 </div>
119
120 <form>
121 <input id="startbutton" type="button" value="Start Session"
122 onclick="start()" />
123 </form>
124
125 <div id="started" style="visibility: hidden">
126 Your session started. Waiting for the results. <br />
127 </div>
128 <div id="results" style="visibility: hidden">
129 Session completed. <a href="" id="logref">view the log file</a> <br />
130 <a href="" id="plotref">render a utilities plot</a>.
131 </div>
132
133</body>
134
135<script type="application/javascript">
136
137
138
139
140
141
142
143
144
145
146
147 // FIXME quick and dirty code. No clean MVC
148
149
150 <![CDATA[
151 //"use strict";
152
153 var domainwebsocket = null;
154 var partieswebsocket=null;
155 // current setting of parameters
156 var parameters = {};
157
158 // currently known domains (and profiles) as coming from domainwebsocket.
159 // keys are domain names, values are list of profile names
160 var knowndomains={};
161
162
163 /**
164 List of created participants for the session. Each participant is a dictionary.
165 Each dict element contains keys
166 "party" and "profile", both containing a String containing
167 a valid IRI to the party resp. the profile to use.
168 The party should contain a IRI that gives a new instance of the required party.
169 The profile should contain an IRI that gives the profile contents.
170 */
171 var partyprofiles=[];
172
173 var cobpartyprofiles=[];
174
175 /** from http://fitzgeraldnick.com/2010/08/10/settimeout-patterns.html */
176
177 function getAdvancedCobSettings() {
178 return document.getElementById('advancedCobSettings').checked;
179 }
180
181 function advancedCobSet() {
182 document.getElementById('advancedsettings').style.display=(getAdvancedCobSettings()?'':'none');
183 }
184
185 function async (fn) {
186 setTimeout(fn, 1000);
187 }
188
189 function sometimeWhen (test, then) {
190 async(function () {
191 if ( test() ) {
192 then();
193 } else {
194 async(arguments.callee);
195 }
196 });
197 }
198
199 /**
200 Called when user changes the protocol */
201 function selectProtocol() {
202 var visible=getSelectedProtocol() == "SHAOP";
203
204 document.getElementById("cobsetting").style.display=(visible ? 'block': 'none');
205 var tbl = document.getElementById('selectedpartiestable');
206 tbl.getElementsByTagName('col')[3].style.visibility=(visible?'':'collapse');
207 tbl.getElementsByTagName('col')[4].style.visibility=(visible?'':'collapse');
208 tbl.getElementsByTagName('col')[5].style.visibility=(visible?'':'collapse');
209
210 var evaluatorvisible=getSelectedProtocol() == "MOPAC";
211 document.getElementById("votingevaluator").style.display=(evaluatorvisible ? 'block': 'none');
212
213 var evaluator2visible=getSelectedProtocol() == "MOPAC2";
214 document.getElementById("votingevaluator2").style.display=(evaluator2visible ? 'block': 'none');
215
216 }
217
218 /**
219 Refresh known domains using given profilesserver URL.
220 Called when user enters URL for domain server.
221 */
222 function connectDomain() {
223 if (domainwebsocket!=null) {
224 domainwebsocket.close();
225 domainwebsocket=null;
226 }
227 var url=new URL("http:"+document.getElementById("profilesserverurl").value);
228 // insert the liststream to the path
229 var target = "ws://"+url.host+url.pathname+"/websocket/liststream"+window.location.search+url.hash;
230 if ('WebSocket' in window) {
231 domainwebsocket = new WebSocket(target);
232 } else if ('MozWebSocket' in window) {
233 domainwebsocket = new MozWebSocket(target);
234 } else {
235 alert('WebSocket is not supported by this browser. Please use a newer browser');
236 return;
237 }
238 domainwebsocket.onopen = function () {
239 // whatever.
240 };
241 domainwebsocket.onmessage = function (event) {
242 updateDomainComboBox(JSON.parse(event.data));
243 };
244 domainwebsocket.onclose = function (event) {
245 alert('Info: Server closed connection. Code: ' + event.code +
246 (event.reason == "" ? "" : ", Reason: " + event.reason));
247 domainwebsocket=null;
248 updateDomainComboBox({});
249 };
250 }
251
252 /**
253 Sets a new knowndomains value and Updates the contents of the domain selector combobox.
254 @param the known domains, a map of the form {"jobs":["jobs1","jobs2"]}
255 where the keys are the names of the available domains nd the values a list of the available profiles in that domain.
256
257 */
258 function updateDomainComboBox(newdomains) {
259 knowndomains=newdomains
260 var combobox = document.getElementById("domainselection");
261 combobox.options.length=0;
262 for (var domain in knowndomains) {
263 var option = document.createElement('option');
264 option.text = option.value = domain;
265 combobox.add(option, 0);
266 }
267 selectDomain();
268 }
269 /**
270 Refresh known parties using given partiesserver URL.
271 Called when user enters URL for parties server.
272 */
273 function connectParties() {
274 if (partieswebsocket!=null) {
275 partieswebsocket.close();
276 partieswebsocket=null;
277 }
278 var url=document.getElementById("partiesserverurl").value;
279 var target = "ws://"+url+"/available";
280 if ('WebSocket' in window) {
281 partieswebsocket = new WebSocket(target);
282 } else if ('MozWebSocket' in window) {
283 partieswebsocket = new MozWebSocket(target);
284 } else {
285 alert('WebSocket is not supported by this browser. Please use a newer browser');
286 return;
287 }
288 partieswebsocket.onopen = function () {
289 // whatever.
290 };
291 partieswebsocket.onmessage = function (event) {
292 updateParties(JSON.parse(event.data));
293 };
294 partieswebsocket.onclose = function (event) {
295 alert('Info: Server closed connection. Code: ' + event.code +
296 (event.reason == "" ? "" : ", Reason: " + event.reason));
297 partieswebsocket=null;
298 updateParties({});
299 };
300 }
301
302
303 /**
304 refresh table: copy all parties elements in there.
305 Typically parties is something like
306 [{"uri":"http:130.161.180.1:8080/partiesserver/run/randomparty-1.5.9",
307 "capabilities":{"protocols":["SAOP"]},
308 "description":"places random bids until it can accept an offer with utility >0.6",
309 "id":"randomparty-1.5.9",
310 "partyClass":"geniusweb.exampleparties.randomparty.RandomParty"},
311 ...]
312 */
313 function updateParties(parties) {
314 updatePartiesCombobox(parties, document.getElementById("partyselection"),['SAOP','AMOP','SHAOP','MOPAC', 'MOPAC2']);
315 updatePartiesCombobox(parties, document.getElementById("cobpartyselection"),['COB']);
316 }
317
318 /**
319 @param parties a list of parties r(as received from the server)
320 @param combobox a combobox element to put the party URLs in
321 @param behaviours a list of behaviours for the parties. Only these are put in the combobox.
322 */
323 function updatePartiesCombobox(parties, combobox, behaviours) {
324 combobox.options.length=0;
325 for (var p in parties) {
326 var party = parties[p];
327 if (intersect(party.capabilities.behaviours, behaviours).length==0)
328 continue;
329 var option = document.createElement('option');
330 option.text = option.value = party.uri;
331 combobox.add(option, 0);
332 }
333
334 }
335
336 /**
337 @param a a list
338 @param b another list
339 @return intersection of a and b
340 */
341 function intersect(a, b) {
342 var t;
343 if (b.length > a.length) t = b, b = a, a = t; // indexOf to loop over shorter
344 return a.filter(function (e) {
345 return b.indexOf(e) > -1;
346 });
347 }
348
349
350 /**
351 updates parameters field to match the given text.
352 */
353 function updateParameters() {
354 var text="{"+document.getElementById("parameters").value+"}";
355 try {
356 parameters=JSON.parse(text);
357 } catch(e) {
358 alert("Parameters can not be parsed. "+e);
359 return;
360 }
361 }
362
363 /**
364 Called when the selected domain changes. Assumes knowndomains has been set.
365 Updates the available profiles in the profile combobox.
366 @param selection the name of the selected domain.
367 */
368 function selectDomain() {
369 // determined current selection
370 var domaincombobox = document.getElementById("domainselection");
371 if (domaincombobox.options.length==0) return; // fixme clean profiles options?
372 var domain = domaincombobox.options[domaincombobox.selectedIndex].value;
373
374 updateProfileComboBox(document.getElementById("profileselection"), knowndomains[domain]);
375 updateProfileComboBox(document.getElementById("cobprofileselection"), knowndomains[domain]);
376 }
377
378 function updateProfileComboBox(profilecombo, options) {
379 profilecombo.options.length=0;
380 for (var profile in options) {
381 var option = document.createElement('option');
382 option.text = option.value = options[profile];
383 profilecombo.add(option, 0);
384 }
385
386 }
387
388 /**
389 Called when user clicks "Add"
390 */
391 function addParty() {
392 addNormalParty();
393 addCobParty();
394 updatePartyProfileTable(); // what, MVC?
395 }
396
397 function addNormalParty() {
398 var partycombo = document.getElementById("partyselection");
399 var profilecombo = document.getElementById("profileselection");
400 var filteroptions = document.getElementById("filter").value;
401
402 if (partycombo.options.length==0) {
403 alert("Please set partier server and select a party");
404 return;
405 }
406 if (profilecombo.options.length==0) {
407 alert("Please set domain/profile server and select a domain and a profile");
408 return;
409 }
410
411 if (filteroptions!="") {
412 filteroptions="?"+filteroptions;
413 }
414 var newpartyprof = {};
415 newpartyprof["party"]={"partyref":partycombo.options[partycombo.selectedIndex].value ,
416 "parameters":parameters };
417 newpartyprof["profile"]=profilecombo.options[profilecombo.selectedIndex].value +filteroptions;
418
419 partyprofiles.push(newpartyprof)
420 }
421
422
423 function addCobParty() {
424 // we assume a sensible default has been loaded into the combo and set,
425 // regardless it being invisible
426 var partycombo = document.getElementById("cobpartyselection");
427 if (partycombo.options.length==0) {
428 alert("Please set cpb partier server and select a party");
429 return;
430 }
431
432 var newpartyprof = {};
433 if (getAdvancedCobSettings()) {
434 var profilecombo = document.getElementById("cobprofileselection");
435 var filteroptions = document.getElementById("cobfilter").value;
436
437 if (profilecombo.options.length==0) {
438 alert("Please set a cob profile");
439 return;
440 }
441 if (filteroptions!="") {
442 filteroptions="?"+filteroptions;
443 }
444 newpartyprof["party"]={"partyref":partycombo.options[partycombo.selectedIndex].value ,
445 "parameters":parameters };
446 newpartyprof["profile"]=profilecombo.options[profilecombo.selectedIndex].value +filteroptions;
447 } else {
448 var profilecombo = document.getElementById("profileselection");
449 newpartyprof["party"]={"partyref":partycombo.options[partycombo.selectedIndex].value ,
450 "parameters":{} };
451 newpartyprof["profile"]=profilecombo.options[profilecombo.selectedIndex].value;
452 }
453 cobpartyprofiles.push(newpartyprof)
454 }
455
456
457 /** updates the party and profiles table, to match the #partyprofiles list. */
458 function updatePartyProfileTable() {
459 var table = document.getElementById("partiesList");
460 table.innerHTML = ""; // clear table
461 for ( var pp in partyprofiles) {
462 var row = table.insertRow(-1);
463 var cell1 = row.insertCell(-1);
464 var cell2 = row.insertCell(-1);
465 var cell3 = row.insertCell(-1);
466 var cell4 = row.insertCell(-1);
467 var cell5 = row.insertCell(-1);
468 var cell6 = row.insertCell(-1);
469
470 cell1.innerHTML = partyprofiles[pp]["party"]["partyref"];
471 // help browser breaking too large strings
472 cell2.innerHTML = JSON.stringify(partyprofiles[pp]["party"]["parameters"]).replace(/,/g,", ");
473 cell2.setAttribute("style","overflow-wrap: anywhere;");
474 cell3.innerHTML = partyprofiles[pp]["profile"];
475 cell4.innerHTML = cobpartyprofiles[pp]["party"]["partyref"];
476 cell5.innerHTML = JSON.stringify(cobpartyprofiles[pp]["party"]["parameters"]).replace(/,/g,", ");
477 cell5.setAttribute("style","overflow-wrap: anywhere;");
478 cell6.innerHTML = cobpartyprofiles[pp]["profile"];
479 }
480
481 }
482
483 var x=1;
484 /**
485 start the session as currently set on this page.
486 We need to send a SessionSettings object to the server, which typically looks like this
487 but is protocol dependent (currently we do SAOP)
488
489 {"SAOPSettings":
490 {"participants":[
491 {"party":{"partyref":"http://party1","parameters":{}},"profile":"ws://profile1"},
492 {"party":{"partyref",}"http://party2","parameters":{}},"profile":"ws://profile2"}],
493 "deadline":{"deadlinetime":{"durationms":100}}}}
494
495 participants are already in the global partyprofiles dictionary
496 */
497 function start() {
498 if (Object.keys(partyprofiles).length <2) {
499 alert("At least 2 parties are needed to run a session.");
500 return;
501 }
502
503 // see https://www.w3schools.com/xml/dom_httprequest.asp
504 document.getElementById("startbutton").disabled=true;
505 document.getElementById("started").setAttribute("style","");
506
507 var xmlHttp = new XMLHttpRequest();
508 xmlHttp.onreadystatechange = function() {
509 if (this.readyState == 4) {
510 if (this.status == 200) {
511 var logurl="log/"+this.responseText+".json";
512 document.getElementById("logref").href=logurl;
513 document.getElementById("plotref").href="plotlog.xhtml"+
514 combineQuery("?id="+this.responseText,window.location.search);
515
516 sometimeWhen(function() { return urlExists(logurl) },
517 function() { document.getElementById("results").setAttribute("style",""); });
518
519 }
520 else
521 alert("request failed:"+this.statusText);
522 }
523 }
524 xmlHttp.open("POST", "run", true);
525 xmlHttp.send(makeRequest());
526
527 }
528
529
530
531 /**
532 @param query1 a query string like &p=q (part of URL)
533 @param query2 another query string
534 @return the combined querystring of query1 and query2
535 */
536 function combineQuery(query1, query2) {
537 if (query1=="") return query2;
538 if (query2=="") return query1;
539 return query1+"&"+query2.substring(1);
540 }
541
542
543 /**
544 @return true iff the URL exists.
545 */
546 function urlExists(urlToFile) {
547 // Warning. Synchronous XMLHttpRequest on the main thread is
548 // deprecated because of its detrimental effects to the end user’s
549 // experience. For more help http://xhr.spec.whatwg.org/
550 var xhr = new XMLHttpRequest();
551 xhr.open('HEAD', urlToFile, false);
552 xhr.send();
553
554 return (xhr.status == "200")
555 }
556
557 function getSelectedProtocol() {
558 var protocolcombo = document.getElementById("selectedprotocol");
559 return protocolcombo.options[protocolcombo.selectedIndex].value;
560 }
561 /**
562 @return a json request package for the run server
563 */
564 function makeRequest() {
565 switch(getSelectedProtocol()) {
566 case "SHAOP":
567 return makeShaopRequest();
568 case"SAOP":
569 case "AMOP":
570 return makeStdRequest(getSelectedProtocol()+"Settings");
571 case "MOPAC":
572 return makeMopacRequest();
573 case "MOPAC2":
574 return makeMopac2Request();
575 }
576 }
577
578 /**
579 @return a SAOP/AMOP request
580 The header contains 'SAOSettings' or 'AMOPSettings'
581 */
582 function makeStdRequest(header) {
583 return JSON.stringify({[header]: standardHeader() });
584 }
585
586 /**
587 * @ereturn MOPAC request. Almost standard but extra 'votingEvaluator'.
588 */
589 function makeMopacRequest() {
590 var extended = standardHeader();
591 var combo = document.getElementById("selectedevaluator");
592 var evaluator = combo.options[combo.selectedIndex].value;
593 extended['votingevaluator'] = { [evaluator]: {} };
594 return JSON.stringify({"MOPACSettings": extended });
595 }
596
597 /**
598 * @ereturn MOPAC2 request. Almost standard but extra 'votingEvaluator'.
599 */
600 function makeMopac2Request() {
601 var extended = standardHeader();
602 var combo = document.getElementById("selectedevaluator2");
603 var evaluator = combo.options[combo.selectedIndex].value;
604 extended['votingevaluator'] = { [evaluator]: {} };
605 return JSON.stringify({"MOPAC2Settings": extended });
606 }
607
608 /**
609 @return standard header with participants and deadline
610 */
611 function standardHeader() {
612 var deadline={};
613 var value = document.getElementById("deadlinevalue").value;
614 var dtypecombo = document.getElementById("deadlinetype");
615 if (dtypecombo.options[dtypecombo.selectedIndex].value=="TIME") {
616 deadline["deadlinetime"] = { "durationms": 1000*value};
617 } else {
618 // ROUNDS
619 deadline["deadlinerounds"] = {"rounds": value, "durationms":10000};
620 }
621 var parties=[];
622 for (let i=0; i< partyprofiles.length; i++) {
623 parties.push( {"TeamInfo":{"parties": [ partyprofiles[i] ] }} );
624 }
625 return { "participants": parties, "deadline":deadline }
626 }
627
628
629
630 /**
631 @return a SHAOP request
632 */
633 function makeShaopRequest() {
634 var deadline={};
635 var value = document.getElementById("deadlinevalue").value;
636 var dtypecombo = document.getElementById("deadlinetype");
637 if (dtypecombo.options[dtypecombo.selectedIndex].value=="TIME") {
638 deadline["deadlinetime"] = { "durationms": 1000*value};
639 } else {
640 // ROUNDS
641 deadline["deadlinerounds"] = {"rounds": value, "durationms":10000};
642 }
643
644 var parties=[];
645 for (let i=0; i< partyprofiles.length; i++) {
646 var team=[ partyprofiles[i] , cobpartyprofiles[i] ];
647 parties.push( {"TeamInfo":{"parties": team }} );
648 }
649 return JSON.stringify({"SHAOPSettings": { "participants": parties, "deadline":deadline }});
650 }
651
652
653 /**
654 Initialize the page after html is loaded.
655 */
656 function init() {
657 selectProtocol();
658 document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.5.9"
659 document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.5.9"
660 connectDomain();
661 connectParties();
662
663 }
664 ]]>
665
666
667
668
669
670
671
672
673
674
675</script>
676
677</html>
Note: See TracBrowser for help on using the repository browser.