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