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