Subversion Repositories svnkaklik

Rev

Blame | Last modification | View Log | Download

/*
    AVRcamVIEW: A PC application to test out the functionallity of the
     AVRcam real-time image processing engine.
    Copyright (C) 2004    Brent A. Taylor

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

   For more information on the AVRcamVIEW, please contact:

   taylorba@comcast.net

   or go to www.jrobot.net for more details regarding the system.
*/

package avr.device;

import java.io.*;
import java.nio.*;
import java.util.*;
import javax.comm.*;

import java.awt.*;

import avr.connection.*;
import avr.connection.event.*;
import avr.device.event.*;
import avr.io.*;
import avr.lang.*;

public class Device implements ConnectionListener {

   public static void main(String[] args) throws Exception {
      Device device = new Device();

      SerialParams params = new SerialParams();

      device.setConnection(new SerialConnection("COM1", params));

      device.connect();
      device.sendPing();
      device.sendDumpFrame();
      device.sendGetVersion();
      device.sendSetRegisters(new HashMap());
      device.sendReset();
      device.sendEnableTracking();
      device.sendDisableTracking();
      device.sendSetColorMap(null, null, null);

      try {
         System.in.read();
      } catch(IOException ioe) {
         ioe.printStackTrace();
      }
      device.disconnect();

   }

   private static final String ACK = "ACK";
   private static final String NCK = "NCK";
   private static final String VERSION = "AVR";

   private static final String COLOR_MAP_KEY = "avr.color.map";

   // timeout after 3 seconds
   private static final int RESPONSE_TIMEOUT = 3000;

   private java.util.List connectionListeners;
   private java.util.List dataListeners;
   private AbstractConnection con;

   private InputStream in;
   private OutputStream out;

   private int[][] colorMap;
   private Color[] mapColors;

   private Timer responseTimer;
   private TimerTask responseTask;

   public Device() {
      connectionListeners = new ArrayList(3);
      dataListeners = new ArrayList(3);
      loadMap();

      responseTimer = new Timer();
   }

   public Color[] getMapColors() {
      return mapColors;
   }

   public int[][] getColorMap() {
      return colorMap;
   }

   public void setColorMap(int[][] colorMap) {
      this.colorMap = colorMap;
      setMapColors();
      saveMap();
   }

   public void saveMap() {
      try {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
         DataOutputStream dOut = new DataOutputStream(bOut);

         dOut.writeInt(colorMap.length);
         for(int i = 0; i < colorMap.length; i++) {
            dOut.writeInt(colorMap[i].length);
            for(int j = 0; j < colorMap[i].length; j++) {
               dOut.writeInt(colorMap[i][j]);
            }
         }

         dOut.close();

         AVRSystem.PREFS.putByteArray(COLOR_MAP_KEY, bOut.toByteArray());

      } catch(Exception e) {
         e.printStackTrace();
      }
   }

   public void loadMap() {
      try {
         byte[] data = AVRSystem.PREFS.getByteArray(COLOR_MAP_KEY, null);
         if(data != null) {
            ByteArrayInputStream bIn = new ByteArrayInputStream(data);
            DataInputStream dIn = new DataInputStream(bIn);

            int width = dIn.readInt();
            colorMap = new int[width][];
            for(int i = 0; i < colorMap.length; i++) {
               colorMap[i] = new int[dIn.readInt()];
               for(int j = 0; j < colorMap[i].length; j++) {
                  colorMap[i][j] = dIn.readInt();
               }
            }

            dIn.close();
         } else {
            colorMap = new int[3][AVRSystem.NUM_INTENSITIES];
         }

         setMapColors();

      } catch(Exception e) {
         e.printStackTrace();
      }

   }

   private void setMapColors() {
      mapColors = new Color[8];

      for(int col = 0; col < mapColors.length; col++) {
         int value = 0;

         int red = 0;
         int green = 0;
         int blue = 0;

         int numRed = 0;
         int numGreen = 0;
         int numBlue = 0;


         for(int i = 0; i < AVRSystem.NUM_INTENSITIES; i++) {

            if((colorMap[0][i] & (0x01 << (7 - col))) != 0) {
               red += i << 4;
               numRed++;
            }

            if((colorMap[1][i] & (0x01 << (7 - col))) != 0) {
               green += i << 4;
               numGreen++;
            }

            if((colorMap[2][i] & (0x01 << (7 - col))) != 0) {
               blue += i << 4;
               numBlue++;
            }

         }

         if(numRed > 0) {
            red /= numRed;
         }

         if(numGreen > 0) {
            green /= numGreen;
         }

         if(numBlue > 0) {
            blue /= numBlue;
         }

         value = (red << 16) | (green << 8) | blue;

         mapColors[col] = new Color(value);
      }

   }

   public AbstractConnection getConnection() {
      return con;
   }

   public void setConnection(AbstractConnection con) {
      this.con = con;
   }

   public boolean isConnected() {
      return con != null && con.isConnected();
   }

   public void addConnectionListener(ConnectionListener listener) {
      AVRSystem.LOG.finest("Added Connection Listener: " + listener);
      connectionListeners.add(listener);
   }

   public void removeConnectionListener(ConnectionListener listener) {
      AVRSystem.LOG.finest("Removed Connection Listener: " + listener);
      connectionListeners.remove(listener);
   }

   public void connected(ConnectionEvent event) {
      ConnectionListener[] listeners = (ConnectionListener[])connectionListeners.toArray(new ConnectionListener[connectionListeners.size()]);
      for(int i = 0; i < listeners.length; i++) {
         ((ConnectionListener)listeners[i]).connected(event);
      }
   }

   public void disconnected(ConnectionEvent event) {
      ConnectionListener[] listeners = (ConnectionListener[])connectionListeners.toArray(new ConnectionListener[connectionListeners.size()]);
      for(int i = 0; i < listeners.length; i++) {
         ((ConnectionListener)listeners[i]).disconnected(event);
      }
   }

   public void connect() throws Exception {

      if(!isConnected()) {
         con.connect();

         if(con instanceof SerialConnection) {
            SerialPort serialPort = (SerialPort)con.getConnectionObject();
            serialPort.notifyOnDataAvailable(true);
            try {
               serialPort.addEventListener(new SerialEventHandler(this));
            } catch(TooManyListenersException tmle) {
               AVRSystem.LOG.severe(tmle.getMessage());
            }
         }

         in = new AVRInputStream(con.getInputStream());
         out = con.getOutputStream();

         AVRSystem.LOG.config("Device connected to " + con.toString());

         connected(new ConnectionEvent(con));

      }
   }

   public void disconnect() {

      if(isConnected()) {
         try {
            con.disconnect();
            AVRSystem.LOG.config("Device Disconnected");
            disconnected(new ConnectionEvent(con));
            con = null;
         } catch(IOException ioe) {
            AVRSystem.LOG.severe(ioe.getMessage());
         }
      }
   }

   public InputStream getInputStream() {
      return in;
   }

   public void addDataListener(DataListener dl) {
      if(dataListeners.add(dl)) {
         AVRSystem.LOG.finest("Added Data Listener: " + dl);
      }
   }

   public void removeDataListener(DataListener dl) {
      if(dataListeners.remove(dl)) {
         AVRSystem.LOG.finest("Removed Data Listener: " + dl);
      }
   }

   protected void handleString(String data) {
      if(data.equals(ACK)) {
         fireACKReceived();
      } else if(data.equals(NCK)) {
         fireNCKReceived();
      } else if(data.startsWith(VERSION)) {
         fireVERSIONReceived(data);
      } else {
         StringBuffer builder = new StringBuffer("UNKNOWN PACKET: (");
         ByteBuffer bytes = ByteBuffer.wrap(data.getBytes());
         builder.append(bytes.remaining()).append(") ");
         while(bytes.hasRemaining()) {
            builder.append(Integer.toHexString(bytes.get() & 0xFF)).append(' ');
         }
         AVRSystem.LOG.warning(builder.toString());
      }
   }

   protected void handleData(ByteBuffer data) {
      if(data.hasRemaining()) {
         byte dataType = data.get();
         if(dataType == 0x0B) {
            fireFrameDataReceived(data);
         } else if(dataType == 0x0A) {
            fireTrackingDataReceived(data);
         } else {
            handleString(new String(data.array(), 0, data.limit()));
         }
      }
   }

   protected void fireACKReceived() {
      if(responseTask != null) {
         responseTask.cancel();
      }
      AVRSystem.LOG.info("Received: ACK");
      DataListener[] listeners = (DataListener[])dataListeners.toArray(new DataListener[dataListeners.size()]);
      for(int i = 0; i < listeners.length; i++) {
         ((DataListener)listeners[i]).ack();
      }
   }

   private void fireResponseTimerExpired() {
      AVRSystem.LOG.severe("Response Timer Expired");
      DataListener[] listeners = (DataListener[])dataListeners.toArray(new DataListener[dataListeners.size()]);
      for(int i = 0; i < listeners.length; i++) {
         ((DataListener)listeners[i]).responseTimerExpired();
      }
   }

   protected void fireNCKReceived() {
      if(responseTask != null) {
         responseTask.cancel();
      }
      AVRSystem.LOG.info("Received: NCK");
      DataListener[] listeners = (DataListener[])dataListeners.toArray(new DataListener[dataListeners.size()]);
      for(int i = 0; i < listeners.length; i++) {
         ((DataListener)listeners[i]).nck();
      }
   }

   protected void fireVERSIONReceived(String version) {
      if(responseTask != null) {
         responseTask.cancel();
      }
      AVRSystem.LOG.info("Received: " + version);
      DataListener[] listeners = (DataListener[])dataListeners.toArray(new DataListener[dataListeners.size()]);
      for(int i = 0; i < listeners.length; i++) {
         ((DataListener)listeners[i]).version(version);
      }
   }

   protected void fireFrameDataReceived(ByteBuffer data) {

      int position = data.position();
      StringBuffer buf = new StringBuffer("Received: Frame Dump (" + (data.get(position) & 0xFF) + ")");

      data.position(0);

      while(data.hasRemaining()) {
         int b = data.get() & 0xFF;
         buf.append(' ').append(((b & 0xF0) == 0) ? "0" : "").append(Integer.toHexString(b).toUpperCase());
      }

      data.position(position);

      AVRSystem.LOG.info(buf.toString());
//      AVRSystem.LOG.info("Received: Frame Dump (" + (data.get(data.position()) & 0xFF) + ")");
      DataListener[] listeners = (DataListener[])dataListeners.toArray(new DataListener[dataListeners.size()]);
      for(int i = 0; i < listeners.length; i++) {
         data.mark();
         ((DataListener)listeners[i]).frameData(data);
         data.reset();
      }
   }

   protected void fireTrackingDataReceived(ByteBuffer data) {

      int position = data.position();
      StringBuffer buf = new StringBuffer("Received: Tracking Info (" + (data.get(position) & 0xFF) + ")");

      data.position(0);

      while(data.hasRemaining()) {
         int b = data.get() & 0xFF;
         buf.append(' ').append(((b & 0xF0) == 0) ? "0" : "").append(Integer.toHexString(b).toUpperCase());
      }

      data.position(position);

      AVRSystem.LOG.info(buf.toString());
      DataListener[] listeners = (DataListener[])dataListeners.toArray(new DataListener[dataListeners.size()]);
      for(int i = 0; i < listeners.length; i++) {
         data.mark();
         ((DataListener)listeners[i]).trackingData(data);
         data.reset();
      }
   }

   private void sendRequest(byte[] data) throws IOException {
      sendRequest(data, 0, data.length);
   }

   private void sendRequest(byte[] data, int off, int len) throws IOException {

      responseTask = new ResponseTask();
      responseTimer.schedule(responseTask, RESPONSE_TIMEOUT);

      if(isConnected()) {

         out.write(data, off, len);
         out.write((byte)'\n');
         out.flush();

         StringBuffer builder = new StringBuffer("Sending: ");
         for(int i = off; i < len; i++) {
            builder.append((char)data[i]);
         }
         AVRSystem.LOG.info(builder.toString());
      } else {
         AVRSystem.LOG.warning("AVRcam not connected.");
      }
   }

   public void sendSetRegisters(Map registers) throws IOException {
      ByteBuffer buffer = ByteBuffer.allocate(60);
      buffer.put((byte)'C')
            .put((byte)'R');

     Set entries = registers.entrySet();
     for(Iterator i = entries.iterator(); i.hasNext();) {
        Map.Entry entry = (Map.Entry)i.next();
        buffer.put((byte)' ').put(entry.getKey().toString().getBytes())
              .put((byte)' ').put(entry.getValue().toString().getBytes());
     }

      buffer.put((byte)'\r');
      buffer.flip();
      sendRequest(buffer.array(), 0, buffer.remaining());
   }

   public void sendDisableTracking() throws IOException {
      sendRequest(new byte[] { (byte)'D', (byte)'T', (byte)'\r'} );
   }

   public void sendDumpFrame() throws IOException {
      sendRequest(new byte[] { (byte)'D', (byte)'F', (byte)'\r'} );
   }

   public void sendEnableTracking() throws IOException {
      sendRequest(new byte[] { (byte)'E', (byte)'T', (byte)'\r'} );
   }

   public void sendGetVersion() throws IOException {
      sendRequest(new byte[] { (byte)'G', (byte)'V', (byte)'\r'} );
   }

   public void sendPing() throws IOException {
      sendRequest(new byte[] { (byte)'P', (byte)'G', (byte)'\r'} );
   }

   public void sendReset() throws IOException {
      sendRequest(new byte[] { (byte)'R', (byte)'S', (byte)'\r'} );
   }

   public void sendSetColorMap(int[] red, int[] green, int[] blue) throws IOException {
      ByteBuffer buffer = ByteBuffer.allocate(2 + 4 * red.length * 3 + 1);
      buffer.put("SM".getBytes());
      for(int i = 0; i < red.length; i++) {
         buffer.put((" " + red[i] + "").getBytes());
      }
      for(int i = 0; i < green.length; i++) {
         buffer.put((" " + green[i] + "").getBytes());
      }
      for(int i = 0; i < blue.length; i++) {
         buffer.put((" " + blue[i] + "").getBytes());
      }
      buffer.put("\r".getBytes());
      buffer.flip();
      sendRequest(buffer.array(), 0, buffer.remaining());
   }

   public void sendCameraData(ByteBuffer data) throws IOException {

      out.write((byte)0x0A);
      out.write(data.array(), 0, data.remaining());
      out.write((byte)0xFF);

      out.flush();

   }

   private final static class SerialEventHandler implements SerialPortEventListener {

      private Device device;

      public SerialEventHandler(Device device) {
         this.device = device;
      }

      public void serialEvent(SerialPortEvent spe) {
         if(spe.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

            try {

               do {

                  byte[] data = new byte[1024];

                  int bytesRead = device.getInputStream().read(data);
                  if(bytesRead > 0) {
                     // take only bytesRead - 1 to remove the
                     // terminating character '\r' or 0xFF
                     EventQueue.invokeLater(new GUITask(device, ByteBuffer.wrap(data, 0, bytesRead - 1)));
                  }

               } while(device.getInputStream().available() > 0);

            } catch(IOException ioe) {
               AVRSystem.LOG.severe(ioe.getMessage());
               device.disconnect();
            }

         }
      }

   }

   private final static class GUITask implements Runnable {
      private ByteBuffer data;
      private Device device;

      public GUITask(Device device, ByteBuffer data) {
         this.device = device;
         this.data = data;
      }

      public void run() {
         device.handleData(data);
      }

   }

   private final class ResponseTask extends TimerTask {

      public void run() {
         // make sure we fire the timer expired event
         // using the Event Dispatch Thread.  If not,
         // queue this TimerTask in the Event Queue.
         if(EventQueue.isDispatchThread()) {
            fireResponseTimerExpired();
         } else {
            EventQueue.invokeLater(this);
         }
      }

   }

   /**
    * Only used for debugging purposes, this will simulate an ACK coming from the device.
    */
   public void simulateACK() {
      fireACKReceived();
   }

   /**
    * Only used for debugging purposes, this will simulate an NCK coming from the device.
    */
   public void simulateNCK() {
      fireNCKReceived();
   }

}