1 package org.opentrafficsim.web;
2
3 import java.net.URL;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.LinkedHashMap;
7 import java.util.List;
8 import java.util.Map;
9
10 import org.djunits.unit.Unit;
11 import org.djunits.value.vdouble.scalar.Duration;
12 import org.djunits.value.vdouble.scalar.Time;
13 import org.djunits.value.vdouble.scalar.base.DoubleScalar;
14 import org.djunits.value.vfloat.scalar.base.FloatScalar;
15 import org.djutils.io.ResourceResolver;
16 import org.eclipse.jetty.ee10.servlet.SessionHandler;
17 import org.eclipse.jetty.io.Content;
18 import org.eclipse.jetty.server.Handler;
19 import org.eclipse.jetty.server.Request;
20 import org.eclipse.jetty.server.Response;
21 import org.eclipse.jetty.server.Server;
22 import org.eclipse.jetty.server.handler.ContextHandler;
23 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
24 import org.eclipse.jetty.server.handler.ResourceHandler;
25 import org.eclipse.jetty.session.DefaultSessionCache;
26 import org.eclipse.jetty.session.DefaultSessionIdManager;
27 import org.eclipse.jetty.session.NullSessionDataStore;
28 import org.eclipse.jetty.session.SessionCache;
29 import org.eclipse.jetty.session.SessionDataStore;
30 import org.eclipse.jetty.util.Callback;
31 import org.eclipse.jetty.util.Fields;
32 import org.opentrafficsim.animation.DefaultAnimationFactory;
33 import org.opentrafficsim.base.logger.Logger;
34 import org.opentrafficsim.core.dsol.OtsAnimator;
35 import org.opentrafficsim.core.dsol.OtsModelInterface;
36 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
37 import org.opentrafficsim.core.perception.HistoryManagerDevs;
38 import org.opentrafficsim.web.test.CircularRoadModel;
39 import org.opentrafficsim.web.test.TJunctionModel;
40
41 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameter;
42 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterBoolean;
43 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDistContinuousSelection;
44 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDistDiscreteSelection;
45 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDouble;
46 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDoubleScalar;
47 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterFloat;
48 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterFloatScalar;
49 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterInteger;
50 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterLong;
51 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterMap;
52 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterSelectionList;
53 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterSelectionMap;
54 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterString;
55
56
57
58
59
60
61
62
63
64 public class TestDemoServer
65 {
66
67 final Map<String, OtsModelInterface> sessionModelMap = new LinkedHashMap<>();
68
69
70 final Map<String, OtsWebModel> sessionWebModelMap = new LinkedHashMap<>();
71
72
73
74
75
76
77 public static void main(final String[] args) throws Exception
78 {
79 new TestDemoServer();
80 }
81
82
83
84
85
86 public TestDemoServer() throws Exception
87 {
88 new ServerThread().start();
89 }
90
91
92 class ServerThread extends Thread
93 {
94
95
96
97 public ServerThread()
98 {
99
100 }
101
102 @Override
103 public void run()
104 {
105 Server server = new Server(8080);
106 ResourceHandler resourceHandler = new MyResourceHandler();
107
108
109 URL homeFolder = ResourceResolver.resolve("/resources/home").asUrl();
110 String webRoot = homeFolder.toExternalForm();
111 Logger.ots().trace("webRoot is " + webRoot);
112
113 resourceHandler.setDirAllowed(true);
114 resourceHandler.setWelcomeFiles(new String[] {"testdemo.html"});
115 resourceHandler.setBaseResourceAsString(webRoot);
116
117 DefaultSessionIdManager idManager = new DefaultSessionIdManager(server);
118 idManager.setWorkerName("testDemoServer");
119 server.addBean(idManager, true);
120
121 SessionHandler sessionHandler = new SessionHandler();
122 SessionCache sessionCache = new DefaultSessionCache(sessionHandler);
123 SessionDataStore sessionDataStore = new NullSessionDataStore();
124 sessionCache.setSessionDataStore(sessionDataStore);
125 sessionHandler.setSessionCache(sessionCache);
126
127 ContextHandler handler1 = new ContextHandler(resourceHandler, "/");
128 ContextHandler handler2 = new ContextHandler(sessionHandler, "/");
129 ContextHandler handler3 = new ContextHandler(new XHRHandler(TestDemoServer.this), "/");
130 ContextHandlerCollection handlers = new ContextHandlerCollection();
131 handlers.setHandlers(new Handler[] {handler1, handler2, handler3});
132 handlers.mapContexts();
133 server.setHandler(handlers);
134
135 try
136 {
137 server.start();
138 server.join();
139 }
140 catch (Exception exception)
141 {
142 exception.printStackTrace();
143 }
144 }
145 }
146
147
148 class MyResourceHandler extends ResourceHandler
149 {
150
151
152
153 MyResourceHandler()
154 {
155
156 }
157
158 @Override
159 public boolean handle(final Request request, final Response response, final Callback callback) throws Exception
160 {
161
162
163
164 String target = request.getHttpURI().getPathQuery();
165
166 if (target.startsWith("/parameters.html"))
167 {
168 Fields fields = Request.getParameters(request);
169 String modelId = fields.getValue("model");
170 String sessionId = fields.getValue("sessionId");
171 if (!TestDemoServer.this.sessionModelMap.containsKey(sessionId))
172 {
173 Logger.ots().trace("parameters: " + modelId);
174 OtsAnimator simulator = new OtsAnimator("TestDemoServer");
175 simulator.setAnimation(false);
176 OtsModelInterface model = null;
177 if (modelId.toLowerCase().contains("circularroad"))
178 model = new CircularRoadModel(simulator);
179 else if (modelId.toLowerCase().contains("tjunction"))
180 model = new TJunctionModel(simulator);
181 if (model != null)
182 TestDemoServer.this.sessionModelMap.put(sessionId, model);
183 else
184 Logger.ots().error("Could not find model " + modelId);
185 }
186 }
187
188 if (target.startsWith("/model.html"))
189 {
190 Fields fields = Request.getParameters(request);
191 String modelId = fields.getValue("model");
192 String sessionId = fields.getValue("sessionId");
193 if (TestDemoServer.this.sessionModelMap.containsKey(sessionId)
194 && !TestDemoServer.this.sessionWebModelMap.containsKey(sessionId))
195 {
196 Logger.ots().trace("startModel: " + modelId);
197 OtsModelInterface model = TestDemoServer.this.sessionModelMap.get(sessionId);
198 OtsSimulatorInterface simulator = model.getSimulator();
199 try
200 {
201 simulator.initialize(Time.ZERO, Duration.ZERO, Duration.ofSI(3600.0), model,
202 HistoryManagerDevs.noHistory(simulator));
203 OtsWebModel webModel = new OtsWebModel(model.getShortName(), simulator);
204 TestDemoServer.this.sessionWebModelMap.put(sessionId, webModel);
205 DefaultAnimationFactory.animateNetwork(model.getNetwork(), model.getNetwork().getSimulator(),
206 webModel.getAnimationPanel().getGtuColorerManager(), Collections.emptyMap());
207 }
208 catch (Exception exception)
209 {
210 exception.printStackTrace();
211 }
212 }
213 }
214
215
216 return super.handle(request, response, callback);
217 }
218 }
219
220
221
222
223
224
225
226
227
228 public static class XHRHandler extends Handler.Abstract
229 {
230
231 private final TestDemoServer webServer;
232
233
234
235
236
237 public XHRHandler(final TestDemoServer webServer)
238 {
239 this.webServer = webServer;
240 }
241
242
243 @Override
244 public boolean handle(final Request request, final Response response, final Callback callback) throws Exception
245 {
246
247
248
249 Fields fields = Request.getParameters(request);
250 String sessionId = fields.getValue("sessionId");
251 if (sessionId != null)
252 {
253 if (this.webServer.sessionWebModelMap.containsKey(sessionId))
254 {
255 boolean handled = this.webServer.sessionWebModelMap.get(sessionId).handle(request, response, callback);
256 if (handled)
257 {
258 return true;
259 }
260 }
261 else if (this.webServer.sessionModelMap.containsKey(sessionId))
262 {
263 OtsModelInterface model = this.webServer.sessionModelMap.get(sessionId);
264 String answer = "<message>ok</message>";
265
266 String message = fields.getValue("message");
267 if (message != null)
268 {
269 String[] parts = message.split("\\|");
270 String command = parts[0];
271
272 switch (command)
273 {
274 case "getTitle":
275 {
276 answer = "<title>" + model.getShortName() + "</title>";
277 break;
278 }
279
280 case "getParameterMap":
281 {
282 answer = makeParameterMap(model);
283 break;
284 }
285
286 case "setParameters":
287 {
288 answer = setParameters(model, message);
289 break;
290 }
291
292 default:
293 {
294 Logger.ots().error("Got unknown message from client: {}", command);
295 answer = "<message>" + request.getAttribute("message") + "</message>";
296 break;
297 }
298 }
299 }
300
301 Content.Sink.write(response, true, answer, callback);
302
303 return true;
304 }
305 }
306
307 return false;
308 }
309
310
311
312
313
314
315 private String makeParameterMap(final OtsModelInterface model)
316 {
317 StringBuffer answer = new StringBuffer();
318 answer.append("<parameters>\n");
319 InputParameterMap inputParameterMap = model.getInputParameterMap();
320 for (InputParameter<?, ?> tab : inputParameterMap.getSortedSet())
321 {
322 if (!(tab instanceof InputParameterMap))
323 {
324 Logger.ots().error("Input parameter {} cannot be displayed in a tab", tab.getShortName());
325 }
326 else
327 {
328 answer.append("<tab>" + tab.getDescription() + "</tab>\n");
329 InputParameterMap tabbedMap = (InputParameterMap) tab;
330 for (InputParameter<?, ?> parameter : tabbedMap.getSortedSet())
331 {
332 addParameterField(answer, parameter);
333 }
334 }
335 }
336 answer.append("</parameters>\n");
337 return answer.toString();
338 }
339
340
341
342
343
344
345 public void addParameterField(final StringBuffer answer, final InputParameter<?, ?> parameter)
346 {
347 if (parameter instanceof InputParameterDouble)
348 {
349 InputParameterDouble pd = (InputParameterDouble) parameter;
350 answer.append("<double key='" + pd.getExtendedKey() + "' name='" + pd.getShortName() + "' description='"
351 + pd.getDescription() + "'>" + pd.getValue() + "</double>\n");
352 }
353 else if (parameter instanceof InputParameterFloat)
354 {
355 InputParameterFloat pf = (InputParameterFloat) parameter;
356 answer.append("<float key='" + pf.getExtendedKey() + "' name='" + pf.getShortName() + "' description='"
357 + pf.getDescription() + "'>" + pf.getValue() + "</float>\n");
358 }
359 else if (parameter instanceof InputParameterBoolean)
360 {
361 InputParameterBoolean pb = (InputParameterBoolean) parameter;
362 answer.append("<boolean key='" + pb.getExtendedKey() + "' name='" + pb.getShortName() + "' description='"
363 + pb.getDescription() + "'>" + pb.getValue() + "</boolean>\n");
364 }
365 else if (parameter instanceof InputParameterLong)
366 {
367 InputParameterLong pl = (InputParameterLong) parameter;
368 answer.append("<long key='" + pl.getExtendedKey() + "' name='" + pl.getShortName() + "' description='"
369 + pl.getDescription() + "'>" + pl.getValue() + "</long>\n");
370 }
371 else if (parameter instanceof InputParameterInteger)
372 {
373 InputParameterInteger pi = (InputParameterInteger) parameter;
374 answer.append("<integer key='" + pi.getExtendedKey() + "' name='" + pi.getShortName() + "' description='"
375 + pi.getDescription() + "'>" + pi.getValue() + "</integer>\n");
376 }
377 else if (parameter instanceof InputParameterString)
378 {
379 InputParameterString ps = (InputParameterString) parameter;
380 answer.append("<string key='" + ps.getExtendedKey() + "' name='" + ps.getShortName() + "' description='"
381 + ps.getDescription() + "'>" + ps.getValue() + "</string>\n");
382 }
383 else if (parameter instanceof InputParameterDoubleScalar)
384 {
385 InputParameterDoubleScalar<?, ?> pds = (InputParameterDoubleScalar<?, ?>) parameter;
386 String val = getValueInUnit(pds);
387 List<String> units = getUnits(pds);
388 answer.append("<doubleScalar key='" + pds.getExtendedKey() + "' name='" + pds.getShortName() + "' description='"
389 + pds.getDescription() + "'><value>" + val + "</value>\n");
390 for (String unit : units)
391 {
392 Unit<?> unitValue = pds.getUnitParameter().getOptions().get(unit);
393 if (unitValue.equals(pds.getUnitParameter().getValue()))
394 answer.append("<unit chosen='true'>" + unit + "</unit>\n");
395 else
396 answer.append("<unit chosen='false'>" + unit + "</unit>\n");
397 }
398 answer.append("</doubleScalar>\n");
399 }
400 else if (parameter instanceof InputParameterFloatScalar)
401 {
402 InputParameterFloatScalar<?, ?> pds = (InputParameterFloatScalar<?, ?>) parameter;
403 String val = getValueInUnit(pds);
404 List<String> units = getUnits(pds);
405 answer.append("<floatScalar key='" + pds.getExtendedKey() + "' name='" + pds.getShortName() + "' description='"
406 + pds.getDescription() + "'><value>" + val + "</value>\n");
407 for (String unit : units)
408 {
409 Unit<?> unitValue = pds.getUnitParameter().getOptions().get(unit);
410 if (unitValue.equals(pds.getUnitParameter().getValue()))
411 answer.append("<unit chosen='true'>" + unit + "</unit>\n");
412 else
413 answer.append("<unit chosen='false'>" + unit + "</unit>\n");
414 }
415 answer.append("</floatScalar>\n");
416 }
417 else if (parameter instanceof InputParameterSelectionList<?>)
418 {
419
420 }
421 else if (parameter instanceof InputParameterDistDiscreteSelection)
422 {
423
424 }
425 else if (parameter instanceof InputParameterDistContinuousSelection)
426 {
427
428 }
429 else if (parameter instanceof InputParameterSelectionMap<?, ?>)
430 {
431
432 }
433 }
434
435
436
437
438
439 private <U extends Unit<U>,
440 T extends DoubleScalar<U, T>> String getValueInUnit(final InputParameterDoubleScalar<U, T> parameter)
441 {
442 return "" + parameter.getDefaultTypedValue().getInUnit(parameter.getDefaultTypedValue().getDisplayUnit());
443 }
444
445
446
447
448
449 private <U extends Unit<U>,
450 T extends DoubleScalar<U, T>> List<String> getUnits(final InputParameterDoubleScalar<U, T> parameter)
451 {
452 List<String> unitList = new ArrayList<>();
453 for (String option : parameter.getUnitParameter().getOptions().keySet())
454 {
455 unitList.add(option.toString());
456 }
457 return unitList;
458 }
459
460
461
462
463
464 private <U extends Unit<U>,
465 T extends FloatScalar<U, T>> String getValueInUnit(final InputParameterFloatScalar<U, T> parameter)
466 {
467 return "" + parameter.getDefaultTypedValue().getInUnit(parameter.getDefaultTypedValue().getDisplayUnit());
468 }
469
470
471
472
473
474 private <U extends Unit<U>,
475 T extends FloatScalar<U, T>> List<String> getUnits(final InputParameterFloatScalar<U, T> parameter)
476 {
477 List<String> unitList = new ArrayList<>();
478 for (String option : parameter.getUnitParameter().getOptions().keySet())
479 {
480 unitList.add(option.toString());
481 }
482 return unitList;
483 }
484
485
486
487
488
489
490
491 private String setParameters(final OtsModelInterface model, final String message)
492 {
493 String errors = "OK";
494 InputParameterMap inputParameters = model.getInputParameterMap();
495 String[] parts = message.split("\\|");
496 Map<String, String> unitMap = new LinkedHashMap<>();
497 for (int i = 1; i < parts.length - 3; i += 3)
498 {
499 String id = parts[i].trim().replaceFirst("model.", "");
500 String type = parts[i + 1].trim();
501 String val = parts[i + 2].trim();
502 if (type.equals("UNIT"))
503 {
504 unitMap.put(id, val);
505 }
506 }
507 for (int i = 1; i < parts.length - 3; i += 3)
508 {
509 String id = parts[i].trim().replaceFirst("model.", "");
510 String type = parts[i + 1].trim();
511 String val = parts[i + 2].trim();
512
513 try
514 {
515 if (type.equals("DOUBLE"))
516 {
517 InputParameterDouble param = (InputParameterDouble) inputParameters.get(id);
518 param.setDoubleValue(Double.valueOf(val));
519 }
520 else if (type.equals("FLOAT"))
521 {
522 InputParameterFloat param = (InputParameterFloat) inputParameters.get(id);
523 param.setFloatValue(Float.valueOf(val));
524 }
525 else if (type.equals("BOOLEAN"))
526 {
527 InputParameterBoolean param = (InputParameterBoolean) inputParameters.get(id);
528 param.setBooleanValue(val.toUpperCase().startsWith("T"));
529 }
530 else if (type.equals("LONG"))
531 {
532 InputParameterLong param = (InputParameterLong) inputParameters.get(id);
533 param.setLongValue(Long.valueOf(val));
534 }
535 else if (type.equals("INTEGER"))
536 {
537 InputParameterInteger param = (InputParameterInteger) inputParameters.get(id);
538 param.setIntValue(Integer.valueOf(val));
539 }
540 else if (type.equals("STRING"))
541 {
542 InputParameterString param = (InputParameterString) inputParameters.get(id);
543 param.setStringValue(val);
544 }
545 if (type.equals("DOUBLESCALAR"))
546 {
547 InputParameterDoubleScalar<?, ?> param = (InputParameterDoubleScalar<?, ?>) inputParameters.get(id);
548 param.getDoubleParameter().setDoubleValue(Double.valueOf(val));
549 String unitString = unitMap.get(id);
550 if (unitString == null)
551 Logger.ots().error("Could not find unit for Doublevalie parameter with id={}", id);
552 else
553 {
554 Unit<?> unit = param.getUnitParameter().getOptions().get(unitString);
555 if (unit == null)
556 Logger.ots().error("Could not find unit {} for Doublevalie parameter with id={}", unitString,
557 id);
558 else
559 {
560 param.getUnitParameter().setObjectValue(unit);
561 param.setCalculatedValue();
562 }
563 }
564 }
565 }
566 catch (Exception exception)
567 {
568 if (errors.equals("OK"))
569 errors = "ERRORS IN INPUT VALUES:\n";
570 errors += "Field " + id + ": " + exception.getMessage() + "\n";
571 }
572 }
573 return errors;
574 }
575
576 }
577
578 }