View Javadoc
1   package org.opentrafficsim.demo.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 javax.servlet.ServletException;
11  import javax.servlet.http.HttpServletRequest;
12  import javax.servlet.http.HttpServletResponse;
13  
14  import org.djunits.unit.Unit;
15  import org.djunits.value.vdouble.scalar.Duration;
16  import org.djunits.value.vdouble.scalar.Time;
17  import org.djunits.value.vdouble.scalar.base.AbstractDoubleScalar;
18  import org.djunits.value.vfloat.scalar.base.AbstractFloatScalar;
19  import org.djutils.cli.Checkable;
20  import org.djutils.cli.CliUtil;
21  import org.djutils.io.URLResource;
22  import org.eclipse.jetty.server.Handler;
23  import org.eclipse.jetty.server.Request;
24  import org.eclipse.jetty.server.Server;
25  import org.eclipse.jetty.server.SessionIdManager;
26  import org.eclipse.jetty.server.handler.AbstractHandler;
27  import org.eclipse.jetty.server.handler.HandlerList;
28  import org.eclipse.jetty.server.handler.ResourceHandler;
29  import org.eclipse.jetty.server.session.DefaultSessionCache;
30  import org.eclipse.jetty.server.session.DefaultSessionIdManager;
31  import org.eclipse.jetty.server.session.NullSessionDataStore;
32  import org.eclipse.jetty.server.session.SessionCache;
33  import org.eclipse.jetty.server.session.SessionDataStore;
34  import org.eclipse.jetty.server.session.SessionHandler;
35  import org.eclipse.jetty.util.resource.Resource;
36  import org.opentrafficsim.core.animation.gtu.colorer.DefaultSwitchableGTUColorer;
37  import org.opentrafficsim.core.dsol.OTSAnimator;
38  import org.opentrafficsim.core.dsol.OTSModelInterface;
39  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
40  import org.opentrafficsim.demo.CircularRoadModel;
41  import org.opentrafficsim.demo.CrossingTrafficLightsModel;
42  import org.opentrafficsim.demo.NetworksModel;
43  import org.opentrafficsim.demo.ShortMerge;
44  import org.opentrafficsim.demo.StraightModel;
45  import org.opentrafficsim.demo.conflict.BusStreetDemo;
46  import org.opentrafficsim.demo.conflict.TJunctionDemo;
47  import org.opentrafficsim.demo.conflict.TurboRoundaboutDemo;
48  import org.opentrafficsim.demo.trafficcontrol.TrafCODDemo1;
49  import org.opentrafficsim.demo.trafficcontrol.TrafCODDemo2;
50  import org.opentrafficsim.draw.factory.DefaultAnimationFactory;
51  
52  import nl.tudelft.simulation.dsol.jetty.sse.OTSWebModel;
53  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameter;
54  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterBoolean;
55  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDistContinuousSelection;
56  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDistDiscreteSelection;
57  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDouble;
58  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDoubleScalar;
59  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterFloat;
60  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterFloatScalar;
61  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterInteger;
62  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterLong;
63  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterMap;
64  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterSelectionList;
65  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterSelectionMap;
66  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterString;
67  import picocli.CommandLine.Command;
68  import picocli.CommandLine.Option;
69  
70  /**
71   * OTSDemoServer.java. <br>
72   * <br>
73   * Copyright (c) 2003-2020 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
74   * for project information <a href="https://www.simulation.tudelft.nl/" target="_blank">www.simulation.tudelft.nl</a>. The
75   * source code and binary code of this software is proprietary information of Delft University of Technology.
76   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
77   */
78  @Command(description = "OTSDemoServer is a web server to run the OTS demos in a browser", name = "OTSDemoServer",
79          mixinStandardHelpOptions = true, version = "1.02.02")
80  public class OTSDemoServer implements Checkable
81  {
82      /** the map of sessionIds to OTSModelInterface that handles the animation and updates for the started model. */
83      @SuppressWarnings("checkstyle:visibilitymodifier")
84      final Map<String, OTSModelInterface> sessionModelMap = new LinkedHashMap<>();
85  
86      /** the map of sessionIds to OTSWebModel that handles the animation and updates for the started model. */
87      @SuppressWarnings("checkstyle:visibilitymodifier")
88      final Map<String, OTSWebModel> sessionWebModelMap = new LinkedHashMap<>();
89  
90      /** the map of sessionIds to the time in msec when the model has to be killed. */
91      @SuppressWarnings("checkstyle:visibilitymodifier")
92      final Map<String, Long> sessionKillMap = new LinkedHashMap<>();
93  
94      /** how many processes max? */
95      @SuppressWarnings("checkstyle:visibilitymodifier")
96      @Option(names = {"-m", "--maxProcesses"}, description = "Maximum number of concurrent demo processes", defaultValue = "10")
97      int maxProcesses;
98  
99      /** how much time max before being killed? */
100     @SuppressWarnings("checkstyle:visibilitymodifier")
101     @Option(names = {"-t", "--killDuration"}, description = "Maximum duration a demo process stays alive before being killed",
102             defaultValue = "10min")
103     Duration killDuration;
104 
105     /** root directory for the web server. */
106     @SuppressWarnings("checkstyle:visibilitymodifier")
107     @Option(names = {"-r", "--rootDirectory"}, description = "Root directory of the web server", defaultValue = "/home")
108     String rootDirectory;
109 
110     /** home page for the web server. */
111     @SuppressWarnings("checkstyle:visibilitymodifier")
112     @Option(names = {"-w", "--homePage"}, description = "Home page for the web server", defaultValue = "superdemo.html")
113     String homePage;
114 
115     /** internet port for the web server. */
116     @SuppressWarnings("checkstyle:visibilitymodifier")
117     @Option(names = {"-p", "--port"}, description = "Internet port to use", defaultValue = "8081")
118     int port;
119 
120     /**
121      * Run a SuperDemo OTS Web server.
122      * @param args String[]; param=value style parameters. Used: maxProcesses=20 maxTimeMinutes=30
123      * @throws Exception on Jetty error
124      */
125     public static void main(final String[] args) throws Exception
126     {
127         OTSDemoServerhtml#OTSDemoServer">OTSDemoServer otsDemoServer = new OTSDemoServer();
128         CliUtil.execute(otsDemoServer, args);
129         otsDemoServer.init();
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public void check() throws Exception
135     {
136         if (this.port <= 0 || this.port > 65535)
137         {
138             throw new Exception("Port should be between 1 and 65535");
139         }
140     }
141 
142     /** Init the server. */
143     private void init()
144     {
145         System.out.println("Kill duration = " + this.killDuration);
146         new ServerThread().start();
147         new KillThread().start();
148     }
149 
150     /**
151      * Constructor to set any variables to default values if needed.
152      */
153     public OTSDemoServer()
154     {
155     }
156 
157     /** Handle the kills of models that ran maxTime minutes. */
158     class KillThread extends Thread
159     {
160         @Override
161         public void run()
162         {
163             while (true)
164             {
165                 try
166                 {
167                     Thread.sleep(10000); // 10 seconds
168                     List<String> kills = new ArrayList<>();
169                     long timeNow = System.currentTimeMillis();
170                     for (String sessionId : OTSDemoServer.this.sessionKillMap.keySet())
171                     {
172                         if (timeNow > OTSDemoServer.this.sessionKillMap.get(sessionId))
173                         {
174                             kills.add(sessionId);
175                         }
176                     }
177                     for (String sessionId : kills)
178                     {
179                         OTSWebModel webModel = OTSDemoServer.this.sessionWebModelMap.get(sessionId);
180                         if (webModel != null)
181                         {
182                             webModel.setKilled(true);
183                             OTSDemoServer.this.sessionWebModelMap.remove(sessionId);
184                         }
185                         OTSModelInterface model = OTSDemoServer.this.sessionModelMap.get(sessionId);
186                         if (model != null)
187                         {
188                             try
189                             {
190                                 model.getSimulator().stop();
191                             }
192                             catch (Exception e)
193                             {
194                                 // ignore
195                             }
196                             OTSDemoServer.this.sessionModelMap.remove(sessionId);
197                         }
198                         OTSDemoServer.this.sessionKillMap.remove(sessionId);
199                     }
200                 }
201                 catch (Exception exception)
202                 {
203                     //
204                 }
205             }
206         }
207     }
208 
209     /** Handle in separate thread to avoid 'lock' of the main application. */
210     class ServerThread extends Thread
211     {
212         @Override
213         public void run()
214         {
215             Server server = new Server(OTSDemoServer.this.port);
216             ResourceHandler resourceHandler = new MyResourceHandler();
217 
218             // root folder; to work in Eclipse, as an external jar, and in an embedded jar
219             URL homeFolder = URLResource.getResource(OTSDemoServer.this.rootDirectory);
220             String webRoot = homeFolder.toExternalForm();
221             System.out.println("webRoot is " + webRoot);
222 
223             resourceHandler.setDirectoriesListed(true);
224             resourceHandler.setWelcomeFiles(new String[] {OTSDemoServer.this.homePage});
225             resourceHandler.setResourceBase(webRoot);
226 
227             SessionIdManager idManager = new DefaultSessionIdManager(server);
228             server.setSessionIdManager(idManager);
229 
230             SessionHandler sessionHandler = new SessionHandler();
231             SessionCache sessionCache = new DefaultSessionCache(sessionHandler);
232             SessionDataStore sessionDataStore = new NullSessionDataStore();
233             sessionCache.setSessionDataStore(sessionDataStore);
234             sessionHandler.setSessionCache(sessionCache);
235 
236             HandlerList handlers = new HandlerList();
237             handlers.setHandlers(new Handler[] {resourceHandler, sessionHandler, new XHRHandler(OTSDemoServer.this)});
238             server.setHandler(handlers);
239 
240             try
241             {
242                 server.start();
243                 server.join();
244             }
245             catch (Exception exception)
246             {
247                 exception.printStackTrace();
248             }
249         }
250     }
251 
252     /** */
253     class MyResourceHandler extends ResourceHandler
254     {
255 
256         /** {@inheritDoc} */
257         @Override
258         public Resource getResource(final String path)
259         {
260             System.out.println(path);
261             if (path.contains("/parameters.html"))
262             {
263                 if (OTSDemoServer.this.sessionModelMap.size() > OTSDemoServer.this.maxProcesses)
264                 {
265                     System.out.println("NO MORE PROCESSES -- MAXMODELS returned");
266                     return super.getResource("/maxmodels.html");
267                 }
268             }
269             return super.getResource(path);
270         }
271 
272         /** {@inheritDoc} */
273         @Override
274         @SuppressWarnings("checkstyle:usebraces")
275         public void handle(final String target, final Request baseRequest, final HttpServletRequest request,
276                 final HttpServletResponse response) throws IOException, ServletException
277         {
278             /*-
279             System.out.println("target      = " + target);
280             System.out.println("baseRequest = " + baseRequest);
281             System.out.println("request     = " + request);
282             System.out.println("request.param " + request.getParameterMap());
283             System.out.println();
284              */
285 
286             if (target.startsWith("/parameters.html"))
287             {
288                 if (OTSDemoServer.this.sessionModelMap.size() > OTSDemoServer.this.maxProcesses)
289                 {
290                     super.handle(target, baseRequest, request, response);
291                     return;
292                 }
293 
294                 String modelId = request.getParameterMap().get("model")[0];
295                 String sessionId = request.getParameterMap().get("sessionId")[0];
296                 if (!OTSDemoServer.this.sessionModelMap.containsKey(sessionId))
297                 {
298                     System.out.println("parameters: " + modelId);
299                     OTSAnimator simulator = new OTSAnimator("OTSDemoServer");
300                     simulator.setAnimation(false);
301                     OTSModelInterface model = null;
302 
303                     if (modelId.toLowerCase().contains("circularroad"))
304                     {
305                         model = new CircularRoadModel(simulator);
306                     }
307                     else if (modelId.toLowerCase().contains("straight"))
308                     {
309                         model = new StraightModel(simulator);
310                     }
311                     else if (modelId.toLowerCase().contains("shortmerge"))
312                     {
313                         model = new ShortMerge.ShortMergeModel(simulator);
314                     }
315                     else if (modelId.toLowerCase().contains("networksdemo"))
316                     {
317                         model = new NetworksModel(simulator);
318                     }
319                     else if (modelId.toLowerCase().contains("crossingtrafficlights"))
320                     {
321                         model = new CrossingTrafficLightsModel(simulator);
322                     }
323                     else if (modelId.toLowerCase().contains("trafcoddemosimple"))
324                     {
325                         URL url = URLResource.getResource("/TrafCODDemo1/TrafCODDemo1.xml");
326                         String xml = TrafCODDemo2.readStringFromURL(url);
327                         model = new TrafCODDemo1.TrafCODModel(simulator, "TrafCODDemo1", "TrafCODDemo1", xml);
328                     }
329                     else if (modelId.toLowerCase().contains("trafcoddemocomplex"))
330                     {
331                         URL url = URLResource.getResource("/TrafCODDemo2/TrafCODDemo2.xml");
332                         String xml = TrafCODDemo2.readStringFromURL(url);
333                         model = new TrafCODDemo2.TrafCODModel(simulator, "TrafCODDemo2", "TrafCODDemo2", xml);
334                     }
335                     else if (modelId.toLowerCase().contains("tjunction"))
336                     {
337                         model = new TJunctionDemo.TJunctionModel(simulator);
338                     }
339                     else if (modelId.toLowerCase().contains("busstreet"))
340                     {
341                         model = new BusStreetDemo.BusStreetModel(simulator);
342                     }
343                     else if (modelId.toLowerCase().contains("turboroundabout"))
344                     {
345                         model = new TurboRoundaboutDemo.TurboRoundaboutModel(simulator);
346                     }
347 
348                     if (model != null)
349                     {
350                         OTSDemoServer.this.sessionModelMap.put(sessionId, model);
351                         long currentMsec = System.currentTimeMillis();
352                         long killMsec = currentMsec + (long) OTSDemoServer.this.killDuration.si * 1000L;
353                         OTSDemoServer.this.sessionKillMap.put(sessionId, killMsec);
354                     }
355                     else
356                     {
357                         System.err.println("Could not find model " + modelId);
358                     }
359                 }
360             }
361 
362             if (target.startsWith("/model.html"))
363             {
364                 String modelId = request.getParameterMap().get("model")[0];
365                 String sessionId = request.getParameterMap().get("sessionId")[0];
366                 if (OTSDemoServer.this.sessionModelMap.containsKey(sessionId)
367                         && !OTSDemoServer.this.sessionWebModelMap.containsKey(sessionId))
368                 {
369                     System.out.println("startModel: " + modelId);
370                     OTSModelInterface model = OTSDemoServer.this.sessionModelMap.get(sessionId);
371                     OTSSimulatorInterface simulator = model.getSimulator();
372                     try
373                     {
374                         simulator.initialize(Time.ZERO, Duration.ZERO, Duration.instantiateSI(3600.0), model);
375                         OTSWebModel webModel = new OTSWebModel(model.getShortName(), simulator);
376                         OTSDemoServer.this.sessionWebModelMap.put(sessionId, webModel);
377                         DefaultAnimationFactory.animateNetwork(model.getNetwork(), simulator,
378                                 new DefaultSwitchableGTUColorer());
379                     }
380                     catch (Exception exception)
381                     {
382                         exception.printStackTrace();
383                     }
384                 }
385             }
386 
387             // handle whatever needs to be done...
388             super.handle(target, baseRequest, request, response);
389         }
390     }
391 
392     /**
393      * Answer handles the events from the web-based user interface for a demo. <br>
394      * <br>
395      * Copyright (c) 2003-2020 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
396      * See for project information <a href="https://www.simulation.tudelft.nl/" target="_blank">www.simulation.tudelft.nl</a>.
397      * The source code and binary code of this software is proprietary information of Delft University of Technology.
398      * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
399      */
400     public static class XHRHandler extends AbstractHandler
401     {
402         /** web server for callback of actions. */
403         private final OTSDemoServer webServer;
404 
405         /**
406          * Create the handler for Servlet requests.
407          * @param webServer DSOLWebServer; web server for callback of actions
408          */
409         public XHRHandler(final OTSDemoServer webServer)
410         {
411             this.webServer = webServer;
412         }
413 
414         /** {@inheritDoc} */
415         @Override
416         public void handle(final String target, final Request baseRequest, final HttpServletRequest request,
417                 final HttpServletResponse response) throws IOException, ServletException
418         {
419             if (request.getParameterMap().containsKey("sessionId"))
420             {
421                 String sessionId = request.getParameterMap().get("sessionId")[0];
422                 if (this.webServer.sessionWebModelMap.containsKey(sessionId))
423                 {
424                     if (this.webServer.sessionWebModelMap.get(sessionId).isKilled())
425                     {
426                         return;
427                     }
428                     this.webServer.sessionWebModelMap.get(sessionId).handle(target, baseRequest, request, response);
429                 }
430                 else if (this.webServer.sessionModelMap.containsKey(sessionId))
431                 {
432                     OTSModelInterface model = this.webServer.sessionModelMap.get(sessionId);
433                     String answer = "<message>ok</message>";
434 
435                     if (request.getParameter("message") != null)
436                     {
437                         String message = request.getParameter("message");
438                         String[] parts = message.split("\\|");
439                         String command = parts[0];
440 
441                         switch (command)
442                         {
443                             case "getTitle":
444                             {
445                                 answer = "<title>" + model.getShortName() + "</title>";
446                                 break;
447                             }
448 
449                             case "getParameterMap":
450                             {
451                                 answer = makeParameterMap(model);
452                                 break;
453                             }
454 
455                             case "setParameters":
456                             {
457                                 answer = setParameters(model, message);
458                                 break;
459                             }
460 
461                             default:
462                             {
463                                 System.err.println("Got unknown message from client: " + command);
464                                 answer = "<message>" + request.getParameter("message") + "</message>";
465                                 break;
466                             }
467                         }
468                     }
469 
470                     response.setContentType("text/xml");
471                     response.setHeader("Cache-Control", "no-cache");
472                     response.setContentLength(answer.length());
473                     response.setStatus(HttpServletResponse.SC_OK);
474                     response.getWriter().write(answer);
475                     response.flushBuffer();
476                     baseRequest.setHandled(true);
477                 }
478             }
479         }
480 
481         /**
482          * Make the parameter set that can be interpreted by the parameters.html page.
483          * @param model the model with parameters
484          * @return an XML string with the parameters
485          */
486         private String makeParameterMap(final OTSModelInterface model)
487         {
488             StringBuffer answer = new StringBuffer();
489             answer.append("<parameters>\n");
490             InputParameterMap inputParameterMap = model.getInputParameterMap();
491             for (InputParameter<?, ?> tab : inputParameterMap.getSortedSet())
492             {
493                 if (!(tab instanceof InputParameterMap))
494                 {
495                     System.err.println("Input parameter " + tab.getShortName() + " cannot be displayed in a tab");
496                 }
497                 else
498                 {
499                     answer.append("<tab>" + tab.getDescription() + "</tab>\n");
500                     InputParameterMap tabbedMap = (InputParameterMap) tab;
501                     for (InputParameter<?, ?> parameter : tabbedMap.getSortedSet())
502                     {
503                         addParameterField(answer, parameter);
504                     }
505                 }
506             }
507             answer.append("</parameters>\n");
508             return answer.toString();
509         }
510 
511         /**
512          * Add the right type of field for this parameter to the string buffer.
513          * @param answer StringBuffer; the buffer to add the XML-info for the parameter
514          * @param parameter InputParameter&lt;?,?&gt;; the input parameter to display
515          */
516         public void addParameterField(final StringBuffer answer, final InputParameter<?, ?> parameter)
517         {
518             if (parameter instanceof InputParameterDouble)
519             {
520                 InputParameterDouble pd = (InputParameterDouble) parameter;
521                 answer.append("<double key='" + pd.getExtendedKey() + "' name='" + pd.getShortName() + "' description='"
522                         + pd.getDescription() + "'>" + pd.getValue() + "</double>\n");
523             }
524             else if (parameter instanceof InputParameterFloat)
525             {
526                 InputParameterFloat pf = (InputParameterFloat) parameter;
527                 answer.append("<float key='" + pf.getExtendedKey() + "' name='" + pf.getShortName() + "' description='"
528                         + pf.getDescription() + "'>" + pf.getValue() + "</float>\n");
529             }
530             else if (parameter instanceof InputParameterBoolean)
531             {
532                 InputParameterBoolean pb = (InputParameterBoolean) parameter;
533                 answer.append("<boolean key='" + pb.getExtendedKey() + "' name='" + pb.getShortName() + "' description='"
534                         + pb.getDescription() + "'>" + pb.getValue() + "</boolean>\n");
535             }
536             else if (parameter instanceof InputParameterLong)
537             {
538                 InputParameterLong pl = (InputParameterLong) parameter;
539                 answer.append("<long key='" + pl.getExtendedKey() + "' name='" + pl.getShortName() + "' description='"
540                         + pl.getDescription() + "'>" + pl.getValue() + "</long>\n");
541             }
542             else if (parameter instanceof InputParameterInteger)
543             {
544                 InputParameterInteger pi = (InputParameterInteger) parameter;
545                 answer.append("<integer key='" + pi.getExtendedKey() + "' name='" + pi.getShortName() + "' description='"
546                         + pi.getDescription() + "'>" + pi.getValue() + "</integer>\n");
547             }
548             else if (parameter instanceof InputParameterString)
549             {
550                 InputParameterString ps = (InputParameterString) parameter;
551                 answer.append("<string key='" + ps.getExtendedKey() + "' name='" + ps.getShortName() + "' description='"
552                         + ps.getDescription() + "'>" + ps.getValue() + "</string>\n");
553             }
554             else if (parameter instanceof InputParameterDoubleScalar)
555             {
556                 InputParameterDoubleScalar<?, ?> pds = (InputParameterDoubleScalar<?, ?>) parameter;
557                 String val = getValueInUnit(pds);
558                 List<String> units = getUnits(pds);
559                 answer.append("<doubleScalar key='" + pds.getExtendedKey() + "' name='" + pds.getShortName() + "' description='"
560                         + pds.getDescription() + "'><value>" + val + "</value>\n");
561                 for (String unit : units)
562                 {
563                     Unit<?> unitValue = pds.getUnitParameter().getOptions().get(unit);
564                     if (unitValue.equals(pds.getUnitParameter().getValue()))
565                         answer.append("<unit chosen='true'>" + unit + "</unit>\n");
566                     else
567                         answer.append("<unit chosen='false'>" + unit + "</unit>\n");
568                 }
569                 answer.append("</doubleScalar>\n");
570             }
571             else if (parameter instanceof InputParameterFloatScalar)
572             {
573                 InputParameterFloatScalar<?, ?> pds = (InputParameterFloatScalar<?, ?>) parameter;
574                 String val = getValueInUnit(pds);
575                 List<String> units = getUnits(pds);
576                 answer.append("<floatScalar key='" + pds.getExtendedKey() + "' name='" + pds.getShortName() + "' description='"
577                         + pds.getDescription() + "'><value>" + val + "</value>\n");
578                 for (String unit : units)
579                 {
580                     Unit<?> unitValue = pds.getUnitParameter().getOptions().get(unit);
581                     if (unitValue.equals(pds.getUnitParameter().getValue()))
582                         answer.append("<unit chosen='true'>" + unit + "</unit>\n");
583                     else
584                         answer.append("<unit chosen='false'>" + unit + "</unit>\n");
585                 }
586                 answer.append("</floatScalar>\n");
587             }
588             else if (parameter instanceof InputParameterSelectionList<?>)
589             {
590                 // TODO InputParameterSelectionList
591             }
592             else if (parameter instanceof InputParameterDistDiscreteSelection)
593             {
594                 // TODO InputParameterSelectionList
595             }
596             else if (parameter instanceof InputParameterDistContinuousSelection)
597             {
598                 // TODO InputParameterDistContinuousSelection
599             }
600             else if (parameter instanceof InputParameterSelectionMap<?, ?>)
601             {
602                 // TODO InputParameterSelectionMap
603             }
604         }
605 
606         /**
607          * @param <U> the unit
608          * @param <T> the scalar type
609          * @param parameter double scalar input parameter
610          * @return default value in the unit
611          */
612         private <U extends Unit<U>,
613                 T extends AbstractDoubleScalar<U, T>> String getValueInUnit(final InputParameterDoubleScalar<U, T> parameter)
614         {
615             return "" + parameter.getDefaultTypedValue().getInUnit(parameter.getDefaultTypedValue().getDisplayUnit());
616         }
617 
618         /**
619          * @param <U> the unit
620          * @param <T> the scalar type
621          * @param parameter double scalar input parameter
622          * @return abbreviations for the units
623          */
624         private <U extends Unit<U>,
625                 T extends AbstractDoubleScalar<U, T>> List<String> getUnits(final InputParameterDoubleScalar<U, T> parameter)
626         {
627             List<String> unitList = new ArrayList<>();
628             for (String option : parameter.getUnitParameter().getOptions().keySet())
629             {
630                 unitList.add(option.toString());
631             }
632             return unitList;
633         }
634 
635         /**
636          * @param <U> the unit
637          * @param <T> the scalar type
638          * @param parameter double scalar input parameter
639          * @return default value in the unit
640          */
641         private <U extends Unit<U>,
642                 T extends AbstractFloatScalar<U, T>> String getValueInUnit(final InputParameterFloatScalar<U, T> parameter)
643         {
644             return "" + parameter.getDefaultTypedValue().getInUnit(parameter.getDefaultTypedValue().getDisplayUnit());
645         }
646 
647         /**
648          * @param <U> the unit
649          * @param <T> the scalar type
650          * @param parameter double scalar input parameter
651          * @return abbreviations for the units
652          */
653         private <U extends Unit<U>,
654                 T extends AbstractFloatScalar<U, T>> List<String> getUnits(final InputParameterFloatScalar<U, T> parameter)
655         {
656             List<String> unitList = new ArrayList<>();
657             for (String option : parameter.getUnitParameter().getOptions().keySet())
658             {
659                 unitList.add(option.toString());
660             }
661             return unitList;
662         }
663 
664         /**
665          * Make the parameter set that can be interpreted by the parameters.html page.
666          * @param model the model with parameters
667          * @param message the key-value pairs of the set parameters
668          * @return the errors if they are detected. If none, errors is set to "OK"
669          */
670         private String setParameters(final OTSModelInterface model, final String message)
671         {
672             String errors = "OK";
673             InputParameterMap inputParameters = model.getInputParameterMap();
674             String[] parts = message.split("\\|");
675             Map<String, String> unitMap = new LinkedHashMap<>();
676             for (int i = 1; i < parts.length - 3; i += 3)
677             {
678                 String id = parts[i].trim().replaceFirst("model.", "");
679                 String type = parts[i + 1].trim();
680                 String val = parts[i + 2].trim();
681                 if (type.equals("UNIT"))
682                 {
683                     unitMap.put(id, val);
684                 }
685             }
686             for (int i = 1; i < parts.length - 3; i += 3)
687             {
688                 String id = parts[i].trim().replaceFirst("model.", "");
689                 String type = parts[i + 1].trim();
690                 String val = parts[i + 2].trim();
691 
692                 try
693                 {
694                     if (type.equals("DOUBLE"))
695                     {
696                         InputParameterDouble param = (InputParameterDouble) inputParameters.get(id);
697                         param.setDoubleValue(Double.valueOf(val));
698                     }
699                     else if (type.equals("FLOAT"))
700                     {
701                         InputParameterFloat param = (InputParameterFloat) inputParameters.get(id);
702                         param.setFloatValue(Float.valueOf(val));
703                     }
704                     else if (type.equals("BOOLEAN"))
705                     {
706                         InputParameterBoolean param = (InputParameterBoolean) inputParameters.get(id);
707                         param.setBooleanValue(val.toUpperCase().startsWith("T"));
708                     }
709                     else if (type.equals("LONG"))
710                     {
711                         InputParameterLong param = (InputParameterLong) inputParameters.get(id);
712                         param.setLongValue(Long.valueOf(val));
713                     }
714                     else if (type.equals("INTEGER"))
715                     {
716                         InputParameterInteger param = (InputParameterInteger) inputParameters.get(id);
717                         param.setIntValue(Integer.valueOf(val));
718                     }
719                     else if (type.equals("STRING"))
720                     {
721                         InputParameterString param = (InputParameterString) inputParameters.get(id);
722                         param.setStringValue(val);
723                     }
724                     if (type.equals("DOUBLESCALAR"))
725                     {
726                         InputParameterDoubleScalar<?, ?> param = (InputParameterDoubleScalar<?, ?>) inputParameters.get(id);
727                         param.getDoubleParameter().setDoubleValue(Double.valueOf(val));
728                         String unitString = unitMap.get(id);
729                         if (unitString == null)
730                             System.err.println("Could not find unit for DoubleScalar parameter with id=" + id);
731                         else
732                         {
733                             Unit<?> unit = param.getUnitParameter().getOptions().get(unitString);
734                             if (unit == null)
735                                 System.err.println(
736                                         "Could not find unit " + unitString + " for DoubleScalar parameter with id=" + id);
737                             else
738                             {
739                                 param.getUnitParameter().setObjectValue(unit);
740                                 param.setCalculatedValue(); // it will retrieve the set double value and unit
741                             }
742                         }
743                     }
744                 }
745                 catch (Exception exception)
746                 {
747                     if (errors.equals("OK"))
748                         errors = "ERRORS IN INPUT VALUES:\n";
749                     errors += "Field " + id + ": " + exception.getMessage() + "\n";
750                 }
751             }
752             return errors;
753         }
754 
755     }
756 
757 }