code: /**
   * Created on 09.11.2004
   * 
   * Copyright (c) 2004-2005, Olaf Landsiedel, Protocol Engineering and 
   * Distributed Systems, University of Tuebingen
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * Redistributions of source code must retain the above copyright notice,
   * this list of conditions and the following disclaimer.
   *
   * Redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution.
   *
   * Neither the name of the University of California, Los Angeles nor the
   * names of its contributors may be used to endorse or promote products
   * derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  package avrora.monitors;
  
  import avrora.sim.Simulator;
  import avrora.sim.mcu.AtmelMicrocontroller;
  import avrora.sim.mcu.USART;
  import avrora.sim.platform.SerialForwarder;
  import avrora.sim.platform.SerialLogger;
  import cck.util.*;
  import java.util.HashMap;
  import java.util.Set;
  import java.util.HashSet;
  
  
  /**
   * The <code>SerialMonitor</code> class is a monitor that that is capable of setting up a virtual
   * usart connection to the pc. You can connect the TinyOS serial forwarder to the port 2390.
   *
   * @author Olaf Landsiedel
   * @author Torsten Landschoff
   */
  public class SerialMonitor extends MonitorFactory {
  
      protected final Option.List PORTS = newOptionList("ports", "0:0:2390",
              "The \"ports\" option specifies a list of server ports that the simulator will listen on " +
              "to connect to the serial forwarder for each node. The format is to first give " +
              "the node number, the UART number, and then the port number " +
              "($node:$uart:$port,$node:$uart:$port).");
      protected final Option.Bool WAITFORCONNECTION = newOption("waitForConnection", false,
              "By default, the serial monitor does not wait for a connection on the given ports. Setting " +
              "this parameter to true mimics the old behaviour where the simulation continued only after " +
              "a successful connection.");
      protected final Option.List DEVICE = newOptionList("devices", "",
              "The \"device\" option can be used to specify the devices (represented as file names) " +
              "to connect to each of the nodes' serial port. The format is to first give " +
              "the node number, the UART number, and then a file name for the input file, and (optionally) " +
              "a file name for the output ($node:$uart:$in[:$out],$node:$uart:$in[:$out]).");
      protected final Option.Str COMMAND = newOption("command", "",
              "The \"command\" option defines an external command to connect to the serial " +
              "port of the simulated system.");
      protected final Option.List TERMINAL = newOptionList("terminal", "",
              "The \"terminal\" option prints packets that are to sent over the UART to the terminal. " +
              "The format is to first give the node number and then the UART number " +
              "($node:$uart,$node:$uart).");
  
      HashMap<Integer, Set<Connection>> portMap;
      private Simulator simulator;
  
      abstract class Connection {
          int usart;
          SerialForwarder sf = null;
          abstract void connect(USART usart);
          void stop() {
              if (sf != null)
                  sf.stop();
          }
      }
  
      class SocketConnection extends Connection {
          int port;
          void connect(USART usart) {
              sf = new SerialForwarder(usart, port, simulator, WAITFORCONNECTION.get());
          }
      }
  
      class FileConnection extends Connection {
          String infile;
          String outfile;
          void connect(USART usart) {
              new SerialForwarder(usart, infile, outfile);
          }
      }
  
      class CommandConnection extends Connection {
          String[] command;
          void connect(USART usart) {
              new SerialForwarder(usart, command);
          }
      }
  
      class TerminalConnection extends Connection {
          void connect(USART usart) {
              new SerialLogger(usart, simulator);
          }
      }
      
      /**
       * The <code>SerialMonitor</code> class is a monitor that connects the USART of a node to a socket that allows data
       * to be read and written from the simulation.
       */
      public class Monitor implements avrora.monitors.Monitor {
  
          /**
           * construct a new monitor
           *
           * @param s Simulator
           */
          Monitor(Simulator s) {
              simulator = s;
              Set<Connection> conns = portMap.get(new Integer(s.getID()));
              if ( conns != null ) {
                  for (Connection conn : conns) {
                      AtmelMicrocontroller mcu = (AtmelMicrocontroller)s.getMicrocontroller();
                      USART usart = (USART)mcu.getDevice("usart" + conn.usart);
                      if ( usart == null && conn.usart == 0 )
                          usart = (USART)mcu.getDevice("usart");
                      conn.connect(usart);
                  }
              }
          }
  
          public void report() {
              //no report, but stop the SocketConnections...
              for (Set<Connection> conns : portMap.values()) {
                  for (Connection conn: conns) {
                      conn.stop();
                  }
              }
          }
  
      }
  
      /**
       * The constructor for the <code>SerialMonitor</code> class builds a new <code>MonitorFactory</code> capable of
       * creating monitors for each <code>Simulator</code> instance passed to the <code>newMonitor()</code> method.
       */
      public SerialMonitor() {
          super("The \"serial\" monitor allows the serial port (UART) of a node in the simulation to be " +
                  "connected to a socket so that data from the program running in the simulation can be " +
                  "outputted, and external data can be fed into the serial port of the simulated node.");
          portMap = new HashMap<Integer, Set<Connection>>();
      }
  
      public void processOptions(Options o) {
          super.processOptions(o);
          processSocketConnections();
          processDeviceConnections();
          processTerminalConnections();
      }
  
      private void processSocketConnections() {
          for (String pid : PORTS.get()) {
              String[] str = pid.split(":");
              if ( str.length < 3 ) Util.userError("Format error in \"ports\" option");
              int nid = Integer.parseInt(str[0]);
              int uart = Integer.parseInt(str[1]);
              int port = Integer.parseInt(str[2]);
              SocketConnection conn = new SocketConnection();
              conn.usart = uart;
              conn.port = port;
              addConnection(nid, conn);
          }
      }
  
      private void processDeviceConnections() {
         for (String pid : DEVICE.get()) {
              String[] str = pid.split(":");
              if ( str.length < 3 ) Util.userError("Format error in \"device\" option");
              int nid = Integer.parseInt(str[0]);
              int uart = Integer.parseInt(str[1]);
              String inf = str[2];
              String outf = (str.length > 3) ? str[3] : inf;
              FileConnection conn = new FileConnection();
              conn.usart = uart;
              conn.infile = inf;
              conn.outfile = outf;
              addConnection(nid, conn);
          }
      }
  
      private void processTerminalConnections() {
          for (String pid : TERMINAL.get()) {
              String[] str = pid.split(":");
              if ( str.length < 2 ) Util.userError("Format error in \"terminal\" option");
              int nid = Integer.parseInt(str[0]);
              int uart = Integer.parseInt(str[1]);
              TerminalConnection conn = new TerminalConnection();
              conn.usart = uart;
              addConnection(nid, conn);
          }
      }
  
      private void addConnection(int nid, Connection ucon) {
          Integer nidI = new Integer(nid);
          Set<Connection> set = portMap.get(nidI);
          if (set == null) {
              set = new HashSet<Connection>();
              portMap.put(nidI, set);
          }
          set.add(ucon);
      }
  
      /**
       * The <code>newMonitor()</code> method creates a new monitor that is capable of setting up a virtual usart
       * connection to the pc. You can connect the TinyOS serial forwarder to the port 2390.
       *
       * @param s the simulator to create a monitor for
       * @return an instance of the <code>Monitor</code> interface for the specified simulator
       */
      public avrora.monitors.Monitor newMonitor(Simulator s) {
          return new Monitor(s);
      }
  
  }
  
  
  
  