@Listen("onClick = button#cfgSave")
 public void cfgSaveClick(MouseEvent event) {
   if (logger.isDebugEnabled()) logger.debug(" cfgSave button event = " + event);
   try {
     Properties config = Util.getConfig(null);
     if (isValidPort(portsToScan.getText())) {
       config.setProperty(Constants.SERIAL_PORTS, portsToScan.getValue());
     } else {
       Messagebox.show(
           "Device ports to scan is invalid. In Linux (pi) '/dev/ttyUSB0,/dev/ttyUSB1,etc', in MS Windows 'COM1,COM2,COM3,etc'");
     }
     if (isValidBaudRate(portBaudRate.getValue())) {
       config.setProperty(Constants.SERIAL_PORT_BAUD, portBaudRate.getValue());
       Util.saveConfig();
     } else {
       Messagebox.show("Device baud rate (speed) must be one of 4800,9600,19200,38400,57600");
     }
     if (NumberUtils.isNumber(cfgWindOffset.getValue())) {
       config.setProperty(Constants.WIND_ZERO_OFFSET, cfgWindOffset.getValue());
       Util.saveConfig();
       // notify others
       producer.sendBody(Constants.WIND_ZERO_ADJUST_CMD + ":" + cfgWindOffset.getValue() + ",");
     } else {
       Messagebox.show("Wind offset must be numeric");
     }
     config.setProperty(Constants.PREFER_RMC, (String) useRmcGroup.getSelectedItem().getValue());
     config.setProperty(
         Constants.DNS_USE_CHOICE, (String) useHomeGroup.getSelectedItem().getValue());
     Util.saveConfig();
   } catch (Exception e) {
     logger.error(e.getMessage(), e);
   }
 }
  private void setConfigDefaults() {
    try {
      Properties config = Util.getConfig(null);

      for (Comboitem item : portBaudRate.getItems()) {
        if (config.getProperty(Constants.SERIAL_PORT_BAUD).equals(item.getValue())) {
          portBaudRate.setSelectedItem(item);
        }
      }
      portsToScan.setValue(config.getProperty(Constants.SERIAL_PORTS));
      cfgWindOffset.setValue(config.getProperty(Constants.WIND_ZERO_OFFSET));
      String useChoice = config.getProperty(Constants.DNS_USE_CHOICE);
      if (Constants.DNS_USE_BOAT.equals(useChoice)) {
        useHomeGroup.setSelectedItem(useBoatRadio);
      } else {
        useHomeGroup.setSelectedItem(useHomeRadio);
      }
      if (new Boolean(config.getProperty(Constants.PREFER_RMC))) {
        useRmcGroup.setSelectedItem(useRmcRadio);
      } else {
        useRmcGroup.setSelectedItem(useHdgRadio);
      }
    } catch (Exception e) {
      logger.error(e.getMessage(), e);
      Messagebox.show("There has been a problem with loading the configuration:" + e.getMessage());
    }
  }
  @Listen("onClick = button#chartSave")
  public void chartSaveClick(MouseEvent event) {
    if (logger.isDebugEnabled()) logger.debug(" chartSave button event = " + event);
    try {
      boolean useHomeChoice = Boolean.valueOf((String) useHomeGroup.getSelectedItem().getValue());
      if (useHomeChoice) {
        Util.getConfig(null).setProperty(Constants.DNS_USE_CHOICE, Constants.DNS_USE_HOME);
      } else {
        Util.getConfig(null).setProperty(Constants.DNS_USE_CHOICE, Constants.DNS_USE_BOAT);
      }

      saveLayers(useHomeChoice);
    } catch (Exception e) {
      logger.error(e.getMessage(), e);
    }
  }
  private void setAllChartLayers() throws Exception {
    File layersDir = new File(Util.getConfig(null).getProperty(Constants.MAPCACHE_RESOURCE));
    for (File layerDir : layersDir.listFiles()) {
      // avoid files starting with dot (.)
      if (layerDir.getName().startsWith(".")) continue;
      if (layerDir.isFile()) continue;
      try {
        File layerFile = new File(layerDir, "freeboard.txt");
        if (layerFile.exists()) {
          String layer = FileUtils.readFileToString(layerFile);
          String chart = layerFile.getName();
          chart = chart.substring(0, chart.indexOf("."));
          String name = getChartName(layer, chart);
          allListMap.put(name, layer);
          if (logger.isDebugEnabled()) logger.debug("Found:" + name);
        } else {
          logger.warn(layerFile.getAbsolutePath() + " does not exist");
        }
      } catch (Exception e) {
        logger.error(e.getMessage(), e);
      }
    }

    allChartsModel = new ListModelList<String>(allListMap.keySet());
  }
 private void setSelectedChartLayers() throws Exception {
   File layersFile =
       new File(Util.getConfig(null).getProperty(Constants.FREEBOARD_RESOURCE) + "/js/layers.js");
   String layer = FileUtils.readFileToString(layersFile);
   // get all the layers in the file
   int pos = layer.indexOf("L.tileLayer(");
   int pos1 = 0;
   while (pos > -1) {
     pos = layer.indexOf("attribution", pos);
     pos = layer.indexOf("'", pos) + 1;
     pos1 = layer.indexOf("'", pos);
     String name = layer.substring(pos, pos1);
     selectedListArray.add(name);
     if (logger.isDebugEnabled()) logger.debug("Found:" + name);
     pos = layer.indexOf("L.tileLayer(", pos1 + 1);
   }
 }
  private void saveLayers(boolean useHomeChoice) throws Exception {

    File layersFile =
        new File(Util.getConfig(null).getProperty(Constants.FREEBOARD_RESOURCE) + "/js/layers.js");
    StringBuffer layers = new StringBuffer();
    layers.append("function addLayers(map) {\n\tvar host = window.location.hostname;\n");
    for (String chart : selectedChartsModel) {
      layers.append(allListMap.get(chart));
      layers.append("\n");
    }
    // layers.append("}\n");
    // now add the layers data
    layers.append("\tbaseLayers = {\n" + "\t\t\"World\": WORLD,\n" + "\t};\n" + "\toverlays = {\n");
    // add the overlays
    for (String chart : selectedChartsModel) {
      String snippet = allListMap.get(chart);
      if (logger.isDebugEnabled()) logger.debug("Processing :" + chart);
      if (logger.isDebugEnabled()) logger.debug(snippet);

      String chartVar = snippet.substring(0, snippet.indexOf("="));
      chartVar = chartVar.substring(chartVar.indexOf("var") + 3).trim();
      snippet = getChartName(snippet, chart);
      layers.append("\t\t\"" + snippet + "\": " + chartVar + ",\n");
    }
    layers.append("\t};\n");
    layers.append("\tlayers = L.control.layers(baseLayers, overlays).addTo(map);\n");
    layers.append("\t};\n");
    String layersStr = layers.toString();
    if (useHomeChoice) {
      // we parse out all refs to the subdomains
      // remove {s}.
      layersStr = StringUtils.remove(layersStr, "{s}.");
      layersStr = StringUtils.remove(layersStr, "subdomains: 'abcd',");
    }
    if (logger.isDebugEnabled()) logger.debug(layersStr);
    FileUtils.writeStringToFile(layersFile, layersStr);
  }
  public ServerMain(String configDir) throws Exception {

    config = Util.getConfig(configDir);
    // make sure we have all the correct dirs and files now
    ensureInstall();

    logger.info("Freeboard starting....");

    // do we have a USB drive connected?
    logger.info("USB drive " + Util.getUSBFile());

    // create a new Camel Main so we can easily start Camel
    Main main = new Main();

    // enable hangup support which mean we detect when the JVM terminates, and stop Camel graceful
    main.enableHangupSupport();

    NavDataWebSocketRoute route = new NavDataWebSocketRoute(config);
    // must do this early!
    CamelContextFactory.setContext(route);
    // web socket on port 9090
    logger.info("  Websocket port:" + config.getProperty(Constants.WEBSOCKET_PORT));
    route.setPort(Integer.valueOf(config.getProperty(Constants.WEBSOCKET_PORT)));

    // are we running demo?
    logger.info("  Serial url:" + config.getProperty(Constants.SERIAL_URL));
    route.setSerialUrl(config.getProperty(Constants.SERIAL_URL));

    // add our routes to Camel
    main.addRouteBuilder(route);

    Connector connector = new SelectChannelConnector();
    logger.info("  Webserver http port:" + config.getProperty(Constants.HTTP_PORT));
    connector.setPort(Integer.valueOf(config.getProperty(Constants.HTTP_PORT)));

    // virtual hosts
    String virtualHosts = config.getProperty(Constants.VIRTUAL_URL);
    server = new Server();
    server.addConnector(connector);

    // serve mapcache
    ServletContextHandler mapContext = new ServletContextHandler();
    logger.info("  Mapcache url:" + config.getProperty(Constants.MAPCACHE));
    mapContext.setContextPath(config.getProperty(Constants.MAPCACHE));
    logger.info("  Mapcache resource:" + config.getProperty(Constants.MAPCACHE_RESOURCE));
    mapContext.setResourceBase(config.getProperty(Constants.MAPCACHE_RESOURCE));
    ServletHolder mapHolder = mapContext.addServlet(DefaultServlet.class, "/*");
    mapHolder.setInitParameter("cacheControl", "max-age=3600,public");

    // serve tracks
    ServletContextHandler trackContext = new ServletContextHandler();
    logger.info("  Tracks url:" + config.getProperty(Constants.TRACKS));
    trackContext.setContextPath(config.getProperty(Constants.TRACKS));
    logger.info("  Tracks resource:" + config.getProperty(Constants.TRACKS_RESOURCE));
    trackContext.setResourceBase(config.getProperty(Constants.TRACKS_RESOURCE));
    trackContext.addServlet(DefaultServlet.class, "/*");

    if (StringUtils.isNotBlank(virtualHosts)) {
      mapContext.setVirtualHosts(virtualHosts.split(","));
      trackContext.setVirtualHosts(virtualHosts.split(","));
    }
    HandlerList handlers = new HandlerList();
    handlers.addHandler(mapContext);
    handlers.addHandler(trackContext);

    // serve freeboard
    WebAppContext wac = new WebAppContext();
    logger.info("  Freeboard resource:" + config.getProperty(Constants.FREEBOARD_RESOURCE));
    wac.setWar(config.getProperty(Constants.FREEBOARD_RESOURCE));
    wac.setDefaultsDescriptor(
        config.getProperty(Constants.FREEBOARD_RESOURCE) + "WEB-INF/webdefault.xml");

    wac.setDescriptor(config.getProperty(Constants.FREEBOARD_RESOURCE) + "WEB-INF/web.xml");
    logger.info("  Freeboard url:" + config.getProperty(Constants.FREEBOARD_URL));
    wac.setContextPath(config.getProperty(Constants.FREEBOARD_URL));
    wac.setServer(server);
    wac.setParentLoaderPriority(true);
    wac.setVirtualHosts(null);
    if (StringUtils.isNotBlank(virtualHosts)) {
      wac.setVirtualHosts(virtualHosts.split(","));
    }
    handlers.addHandler(wac);

    server.setHandler(handlers);
    server.start();

    // and run, which keeps blocking until we terminate the JVM (or stop CamelContext)
    main.run();

    // so now shutdown serial reader and server

    route.stopSerial();
    server.stop();
    System.exit(0);
  }