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