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

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

Minor fixes

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