-Added a class to serve up results to atom. A matching client C++ class should follow shortly.
-Generally beautified code


git-svn-id: https://robotics.mvla.net/svn/frc971/2013/trunk/src@4148 f308d9b7-e957-4cde-b6ac-9a88185e7312
diff --git a/971CV/src/org/frc971/AccepterThread.java b/971CV/src/org/frc971/AccepterThread.java
new file mode 100644
index 0000000..e6cd5dd
--- /dev/null
+++ b/971CV/src/org/frc971/AccepterThread.java
@@ -0,0 +1,153 @@
+/**
+ * 
+ */
+package org.frc971;
+
+/**
+ * @author daniel
+ * Accepts clients for data server
+ */
+
+import java.io.IOException;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+public class AccepterThread extends Thread {
+	
+	private final static Logger LOG = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+	
+	private ServerSocketChannel sock;
+	
+	private List<SocketChannel> connected = new ArrayList<SocketChannel>(); 
+	
+	/* Holds overflow data when socket's send buffer gets full, so that
+	 * thread can continue running.
+	 */
+	private Map<SocketChannel, ByteBuffer> toSend;
+	/* Keeps track of how many times a write operation on a socket
+	 * has failed because it's buffer was full.
+	 */
+	private Map<SocketChannel, Integer> failedAttempts; //doesn't like primitive types
+	
+	/** Helper function to completely erase a peer from
+	 *  all three lists and maps that might contain it.
+	 */
+	private void erasePeer(SocketChannel peer) {
+		connected.remove(peer);
+		toSend.remove(peer);
+		failedAttempts.remove(peer);
+	}
+	
+	/** Constructor
+	 * 
+	 * @param sock is the ServerSocketChannel that you want to monitor
+	 */
+	public AccepterThread(ServerSocketChannel sock) {
+		super("Accepter Thread");
+		setPriority(3); //lowish priority so Image Processor overrides it
+		this.sock = sock;
+		start();
+	}
+	
+	/** Runs in separate thread. Continually accepts new connections. */
+	public void run() {
+		SocketChannel clientSock;
+		while (true) {
+			try {
+				clientSock = sock.accept();
+				//our writes must not block
+				clientSock.configureBlocking(false);
+				connected.add(clientSock);
+			}
+			catch (IOException e) {
+				LOG.warning("Socket accept failed.");
+			}
+		}
+	}
+	
+	/** Sends a message to all currently connected clients.
+	 * 
+	 * @param message is the message that you want to send.
+	 */
+	public void sendtoAll(ByteBuffer message) {
+		/* Copy our connected list, so we don't have 
+		 * to hold our lock forever if the writes block.
+		 */
+		List<SocketChannel> connectedTemp = new ArrayList<SocketChannel>();
+		for (SocketChannel channel : connected) {
+			connectedTemp.add(channel);
+		}
+		
+		int result;
+		for (SocketChannel conn : connectedTemp) {
+			try {
+				
+				/** If this socket has data from the 
+				 * last send operation still waiting to be
+				 * sent, send this instead of our original
+				 * message. Since we generally want only
+				 * current data, our original message will
+				 * not be missed. However, it is imperative
+				 * that we finish our pending transmission, 
+				 * because an incomplete transmission could
+				 * leave a client thread somewhere blocking
+				 * indefinitely.
+				 */
+				if (toSend.containsKey(conn)) {
+					message = toSend.get(conn);
+				}
+				
+				result = conn.write(message);
+				
+				/*if our send buffer is full, store our message away
+				 * so we can try again later without halting the thread.
+				 */
+				if (message.remaining() > 0) {
+					toSend.put(conn, message);
+					//check and update our count of failed send attempts
+					if (failedAttempts.containsKey(conn)) {
+						int failures = failedAttempts.get(conn);
+						++failures;
+						if (failures >= 100) {
+							//Socket has become dysfunctional
+							LOG.info("Write would have blocked 100 times. Assuming peer disconect.");
+							erasePeer(conn);
+						}
+						failedAttempts.put(conn, failures);
+					}
+					else {
+						failedAttempts.put(conn, 1);
+					}
+				}
+				
+				if (result == -1) {
+					//The write failed. This is probably because the client disconnected.
+					LOG.info("Write returned -1. Client has probably disconnected.");
+					erasePeer(conn);
+				}
+			}
+			catch (IOException e) {
+				//The write failed. This is probably because the client disconnected.
+				LOG.info("Write threw IOException. Client has probably disconnected.");
+				erasePeer(conn);
+			}
+		}
+	}
+	
+	/** Overloaded sendtoAll method for byte arrays. */
+	public void sendtoAll(byte[] message) {
+		sendtoAll(ByteBuffer.wrap(message));
+	}
+	
+	/** Overloaded sendtoAll method for Strings. */
+	public void sendtoAll(String message) {
+		sendtoAll(message.getBytes());
+	}
+}
diff --git a/971CV/src/org/frc971/DebugServerRun.java b/971CV/src/org/frc971/DebugServerRun.java
index c8146b1..0f60665 100644
--- a/971CV/src/org/frc971/DebugServerRun.java
+++ b/971CV/src/org/frc971/DebugServerRun.java
@@ -1,27 +1,14 @@
 package org.frc971;
 
 import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.InterruptedIOException;
-import java.io.PrintWriter;
 
 import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
 import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.channels.GatheringByteChannel;
 import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
 
@@ -40,51 +27,8 @@
 		
 		private ServerSocketChannel sock;
 		private SocketChannel client;
-		
-		private BufferedReader sock_in;
-		private PrintWriter sock_out;
-		
-		private String ReadtoBoundary(String boundary) {
-			//reads from socket until it encounters a specific character combination
-			//if boundary is null, it reads until it runs out of data
-			ByteBuffer recvd = ByteBuffer.allocate(1024);
-			StringBuilder sb = new StringBuilder();
-			String message = "";
-			try {
-				int ret = 0;
-				while (ret != -1) {
-					ret = client.read(recvd);
-					//System.out.println(ret);
-					if (ret == 0) {
-						//finished receiving
-						message = sb.toString();
-						if (boundary == null)
-							break;
-					}
-					else {
-						for (int i = 0; i < recvd.capacity() - recvd.remaining(); i++) {
-							sb.append((char)recvd.get(i));
-						}
-						recvd.clear();
-						if (boundary != null) {
-							if (sb.toString().contains(boundary)) {
-								message = sb.toString();
-								break;
-							}
-							else {
-								continue;
-							}
-						}
-					}
-				}
-			}
-			catch (IOException e) {
-				LOG.severe("Socket read failed.");
-				return null;
-			}
-			return message;
-		}
 	
+	/** Constructs a formatted boundary header from a timestamp and content length. */	
 	private ByteBuffer CreateTransmission(long content_length, double timestamp) {
 		StringBuilder ret = new StringBuilder();
 		ret.append("\r\n--boundarydonotcross\r\n");
@@ -97,8 +41,9 @@
         ret.append("\r\n\r\n");
         return ByteBuffer.wrap(ret.toString().getBytes());
 	}
+	
+	/** Loop that pushes a data stream to the client. */
 	private void push() {
-		//push data to client
 		try {
 			grabber.start();
 		}
@@ -116,6 +61,10 @@
 			try {
 				img = grabber.grab();
 				timestamp = System.currentTimeMillis();
+				/*We buffer through /dev/shm, just to make the conversion process easier.
+				 * I know this is really ugly, but it works a lot better than what
+				 * I was doing before, which segfaulted.
+				 */
 				cvSaveImage("/dev/shm/DebugServerBuffer.jpg", img);
 				buff_file = new File("/dev/shm/DebugServerBuffer.jpg");
 				content_size = buff_file.length();
@@ -136,9 +85,7 @@
 		        to_send.put(header);
 		        to_send.put(bbuf);
 		        to_send.rewind();
-		        while (to_send.remaining() > 0) {
-		        	client.write(to_send);
-		        }
+		        SocketCommon.sendAll(client, to_send);
 			}
 			catch (Exception e) {
 				LOG.warning("Could not grab frame.");
@@ -146,35 +93,26 @@
 			}
 		}
 	}
-		
-	public void Connect() throws IOException {
-		client = sock.accept();
-		client.configureBlocking(false);
-		//sock_in = new BufferedReader(new InputStreamReader(client.socket().getInputStream()));
-		//sock_out = new PrintWriter(client.socket().getOutputStream(), true);
-		//we are now connected to our client. Wait for them to send us a header.
-		LOG.info("Reading headers...");
-		ReadtoBoundary("\r\n\r\n");
-		//send one back
-		LOG.info("Writing headers...");
-		String ending = "donotcross\r\n";
-		ByteBuffer buff = ByteBuffer.wrap(ending.getBytes());
-		while (buff.remaining() > 0) {
-			client.write(buff);
-		}
-	}
-	
+	/** Constructor to start the server and bind it to a port. */
 	public DebugServerRun(final int port) throws IOException {
 		sock = ServerSocketChannel.open();
 		sock.socket().bind(new InetSocketAddress(9714));
+		client = sock.accept();
+		client.configureBlocking(false);
+		//we are now connected to our client. Wait for them to send us a header.
+		LOG.info("Reading headers...");
+		SocketCommon.readtoBoundary(client, "\r\n\r\n");
+		//send one back
+		LOG.info("Writing headers...");
+		SocketCommon.sendAll(client, "donotcross\r\n");
 	}
-	public static void main(final String args[]) throws IOException{
+	/** Runs the server, and concurrently starts the vision processor with -vision flag. */
+	public static void main(final String args[]) throws IOException {
 		//main function for server
-		
 		//set logger to log everything
         LOG.setLevel(Level.ALL);
         try {
-        	LogHandler handler = new LogHandler("../src/org/frc971/ds_vision.log");
+        	LogHandler handler = new LogHandler("ds_vision.log");
         	TimeFormatter formatter = new TimeFormatter();
             handler.setFormatter(formatter);
             LOG.addHandler(handler);
@@ -183,9 +121,12 @@
         	System.err.println("Warning: Logging initialization failed.");
         }
         
+		if (args[0].equals("-vision")) {
+			LOG.info("Starting vision processor.");
+			new TestClient();
+		}
+		
 		DebugServerRun server = new DebugServerRun(9714);
-		new TestClient();
-		server.Connect();
 		server.push();
 	}
 }
diff --git a/971CV/src/org/frc971/HTTPClient.java b/971CV/src/org/frc971/HTTPClient.java
index 4c7d0af..cc694ef 100644
--- a/971CV/src/org/frc971/HTTPClient.java
+++ b/971CV/src/org/frc971/HTTPClient.java
@@ -2,28 +2,17 @@
 
 //@author: daniel
 
-import static com.googlecode.javacv.cpp.opencv_highgui.cvEncodeImage;
-
 import java.io.*;
 import java.net.*;
 
 import java.awt.image.BufferedImage;
 
-import java.nio.channels.FileChannel;
 import java.nio.channels.SocketChannel;
 import java.nio.ByteBuffer;
 
-import java.util.Iterator;
 import java.util.logging.Logger;
 
 import javax.imageio.ImageIO;
-import javax.imageio.ImageReadParam;
-import javax.imageio.ImageReader;
-import javax.imageio.stream.ImageInputStream;
-
-import com.googlecode.javacv.cpp.opencv_core.CvArr;
-import com.googlecode.javacv.cpp.opencv_core.CvMat;
-import com.googlecode.javacv.cpp.opencv_core.IplImage;
 
 import aos.ChannelImageGetter;
 
@@ -32,14 +21,10 @@
 public class HTTPClient {
 	//Connects to HTTP Server on robot and receives images
 	
-	private final static boolean LOCAL_DEBUG = true;
+	/** whether or not to print debug messages to stdout. */
+	private final static boolean LOCAL_DEBUG = false;
 	
 	private SocketChannel sock;
-	private Socket core_sock;
-	
-	
-	private BufferedReader sock_in;
-	private PrintWriter sock_out;
 	
 	private final String ATOM_IP = "192.168.0.137";
 	
@@ -47,87 +32,37 @@
 	
 	private final static Logger LOG = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
 	
+	/** Small helper method for printing debug messages to stdout. */
 	private void WriteDebug(String message) {
 		//small helper function to write debug messages
 		if (LOCAL_DEBUG)
 			LOG.info("LOCAL_DEBUG: " + message);
 	}
-	private String ReadtoBoundary(String boundary) {
-		//reads from socket until it encounters a specific character combination
-		//if boundary is null, it reads until it runs out of data
-		ByteBuffer recvd = ByteBuffer.allocate(1024);
-		StringBuilder sb = new StringBuilder();
-		String message = "";
-		try {
-			int ret;
-			while ((ret = sock.read(recvd)) != -1) {
-				if (ret == 0) {
-					//finished receiving
-					message = sb.toString();
-					if (boundary == null)
-						break;
-				}
-				else {
-					for (int i = 0; i < recvd.capacity() - recvd.remaining(); i++) {
-						sb.append((char)recvd.get(i));
-					}
-					recvd.clear();
-					if (boundary != null) {
-						if (sb.toString().contains(boundary)) {
-							message = sb.toString();
-							break;
-						}
-						else {
-							continue;
-						}
-					}
-				}
-			}
-		}
-		catch (IOException e) {
-			LOG.severe("Socket read failed.");
-			return null;
-		}
-		return message;
+	
+	/** the constructor, initializes connection, and sets up aos getter. 
+	 * @throws IOException */
+	public HTTPClient() throws IOException {
+		//Initialize socket connection to robot
+		sock = SocketChannel.open();
+		WriteDebug("Connecting to server at " + ATOM_IP);
+		sock.connect(new InetSocketAddress(ATOM_IP, 9714));
+		sock.configureBlocking(false);
+		//Write headers
+		//HTTPStreamer does not actually use the headers, so we can just write terminating chars.
+		WriteDebug("Writing headers...");
+		SocketCommon.sendAll(sock, "\r\n\r\n");
+		//Receive headers
+		WriteDebug("Reading headers...");
+		SocketCommon.readtoBoundary(sock, "donotcross\r\n");
+		WriteDebug("Now receiving data.");
+		cgetter = new ChannelImageGetter(sock);
 	}
 	
-	public HTTPClient() {
-		//Initialize socket connection to robot
-		try {
-			sock = SocketChannel.open();
-			core_sock = sock.socket();
-			WriteDebug("Connecting to server at " + ATOM_IP);
-			sock.connect(new InetSocketAddress(ATOM_IP, 9714));
-			sock.configureBlocking(false);
-			//sock_in = new BufferedReader(new InputStreamReader(core_sock.getInputStream()));
-			//sock_out = new PrintWriter(core_sock.getOutputStream(), true);
-			//Write headers
-			//HTTPStreamer does not actually use the headers, so we can just write terminating chars.
-			WriteDebug("Writing headers...");
-			String ending = "\r\n\r\n";
-			ByteBuffer header = ByteBuffer.wrap(ending.getBytes());
-			while (header.remaining() > 0) {
-				sock.write(header);
-			}
-			//Receive headers
-			WriteDebug("Reading headers...");
-			ReadtoBoundary("donotcross\r\n");
-			WriteDebug("Now receiving data.");
-			cgetter = new ChannelImageGetter(sock);
-		}
-		catch (UnknownHostException e) {
-			LOG.severe("Invalid host.");
-			System.exit(1);
-		}
-		catch (IOException e) {
-			LOG.severe("Socket IO failed: " + e.getMessage());
-			System.exit(2);
-		}
-		
-	}
+	/** Grabs the most current frame from the HTTPStreamer stream.
+	 * Returns a class instance with image and timestamp attributes. */
 	public ImageWithTimestamp GetFrame() {
-		//Use Brian's code to extract an image and timestamp from raw server data.
 		ImageWithTimestamp final_image = new ImageWithTimestamp();
+		//Use Brian's code to extract an image and timestamp from raw server data.
 		ByteBuffer binary_image = cgetter.getJPEG();
 		//Decode ByteBuffer into an IplImage
 		byte[] b = new byte[binary_image.remaining()];
@@ -141,6 +76,7 @@
 			return final_image;
 		}
 		catch (IOException e) {
+			LOG.warning("Image processing failed: " + e.getMessage());
 			return null;
 		}
 	}	
diff --git a/971CV/src/org/frc971/ImageWithTimestamp.java b/971CV/src/org/frc971/ImageWithTimestamp.java
index 38f6937..8b43818 100644
--- a/971CV/src/org/frc971/ImageWithTimestamp.java
+++ b/971CV/src/org/frc971/ImageWithTimestamp.java
@@ -2,6 +2,7 @@
 
 import edu.wpi.first.wpijavacv.WPIColorImage;
 
+/** Small helper class for associating images and timestamps. */
 public class ImageWithTimestamp {
 	WPIColorImage image;
 	double timestamp;
diff --git a/971CV/src/org/frc971/LogHandler.java b/971CV/src/org/frc971/LogHandler.java
index 59f74a9..fd486fb 100644
--- a/971CV/src/org/frc971/LogHandler.java
+++ b/971CV/src/org/frc971/LogHandler.java
@@ -12,13 +12,20 @@
 
 /**
  * @author daniel
- * logs data to custom files, using specific formatting.
+ * 
  */
+
+/** Logs data to custom files, using specific formatting. */
 public class LogHandler extends Handler {
 	
 	private FileOutputStream ofstream;
 	PrintWriter writer;
 	
+	/** Constructor for log handler. 
+	 * 
+	 * @param filename is the name of the file you want to log to.
+	 * @throws FileNotFoundException if file cannot be opened or created.
+	 */
 	public LogHandler (String filename) throws FileNotFoundException {
 		super();
 		
@@ -34,6 +41,9 @@
 	
 	/*Required methods*/
 	
+	/** Is required by API. Writes a new message to the log.
+	 * @param message is the message you want to log.
+	 */
 	public void publish(LogRecord message) {
 		//record a message
 		if (!isLoggable(message)) {
@@ -42,9 +52,13 @@
 		}
 		writer.print(getFormatter().format(message)); //Formatter adds trailing \n
 	}
+	
+	/** Is required by API. Flushes the writer. */
 	public void flush() {
 		writer.flush();
 	}
+	
+	/** Is required by API. Closes logfile. */
 	public void close() throws SecurityException {
 		writer.close();
 	}
diff --git a/971CV/src/org/frc971/ResultSender.java b/971CV/src/org/frc971/ResultSender.java
new file mode 100644
index 0000000..897371d
--- /dev/null
+++ b/971CV/src/org/frc971/ResultSender.java
@@ -0,0 +1,53 @@
+/**
+ * 
+ */
+package org.frc971;
+
+import java.io.IOException;
+
+import java.net.InetSocketAddress;
+
+import java.nio.channels.ServerSocketChannel;
+
+/**
+ * @author daniel
+ *
+ */
+
+/**  Serves processing results back to the atom. */
+public class ResultSender {
+	private static final int PORT = 9715;
+	
+	private ServerSocketChannel sock;
+	
+	AccepterThread acceptor;
+	
+	/** Constructor. Connects to a socket and starts the accepter thread. */
+	public ResultSender() throws IOException {
+		sock = ServerSocketChannel.open();
+		sock.socket().bind(new InetSocketAddress(PORT));
+		
+		//start accepter thread
+		acceptor = new AccepterThread(sock);
+	}
+	
+	/** Sends a new message of calculated attributes to the clients.
+	 * 
+	 * @param azimuth is the calculated optimum azimuth for the shot.
+	 * @param elevation is the calculated optimum elevation for the shot.
+	 * @param range is the calculated optimum range for the shot.
+	 */
+	public void send(double azimuth, double elevation, double range) {
+		//Formulate a message as a String similar to an HTTP header.
+		StringBuilder message = new StringBuilder();
+		message.append("\r\n--boundarydonotcross\r\n");
+		message.append("Azimuth: ");
+		message.append(azimuth);
+		message.append("\r\nElevation: ");
+		message.append(elevation);
+		message.append("\r\nRange: ");
+		message.append(range);
+		
+		acceptor.sendtoAll(message.toString());
+	}
+}
diff --git a/971CV/src/org/frc971/SocketCommon.java b/971CV/src/org/frc971/SocketCommon.java
new file mode 100644
index 0000000..e49a82b
--- /dev/null
+++ b/971CV/src/org/frc971/SocketCommon.java
@@ -0,0 +1,89 @@
+/**
+ * 
+ */
+package org.frc971;
+
+import java.io.IOException;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.logging.Logger;
+
+/**
+ * @author daniel
+ * Socket operations used by other classes
+ */
+public class SocketCommon {
+
+	private final static Logger LOG = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+	
+	/** Reads on a SocketStream until it finds a given character sequence. */
+	public static String readtoBoundary(SocketChannel sock, String boundary) {
+		//reads from socket until it encounters a specific character combination
+		//if boundary is null, it reads until it runs out of data
+		ByteBuffer recvd = ByteBuffer.allocate(1024);
+		StringBuilder sb = new StringBuilder();
+		String message = "";
+		try {
+			int ret = 0;
+			while (ret != -1) {
+				ret = sock.read(recvd);
+				//System.out.println(ret);
+				if (ret == 0) {
+					//finished receiving
+					message = sb.toString();
+					if (boundary == null)
+						break;
+				}
+				else {
+					for (int i = 0; i < recvd.capacity() - recvd.remaining(); i++) {
+						sb.append((char)recvd.get(i));
+					}
+					recvd.clear();
+					if (boundary != null) {
+						if (sb.toString().contains(boundary)) {
+							message = sb.toString();
+							break;
+						}
+						else {
+							continue;
+						}
+					}
+				}
+			}
+		}
+		catch (IOException e) {
+			LOG.severe("Socket read failed.");
+			return null;
+		}
+		return message;
+	}
+	
+	/** Guarantees that large messages will be completely sent through a socket.
+	 * @return Returns 0 for success, -1 for failure.
+	 */
+	public static int sendAll(SocketChannel sock, ByteBuffer message) {
+		message.rewind();
+		while (message.remaining() > 0) {
+			try {
+				sock.write(message);
+			}
+			catch (IOException e) {
+				LOG.warning("Socket write failed.");
+				return -1;
+			}
+		}
+		return 0;
+	}
+	
+	/** Overloaded method for sending a byte array. */
+	public static void sendAll(SocketChannel sock, byte[] message) {
+		ByteBuffer buff = ByteBuffer.wrap(message);
+		sendAll(sock, buff);
+	}
+	
+	/** Overloaded method for sending a String. */
+	public static void sendAll(SocketChannel sock, String message) {
+		sendAll(sock, message.getBytes());
+	}
+}
diff --git a/971CV/src/org/frc971/TestClient.java b/971CV/src/org/frc971/TestClient.java
index fed5c27..926a817 100644
--- a/971CV/src/org/frc971/TestClient.java
+++ b/971CV/src/org/frc971/TestClient.java
@@ -5,13 +5,19 @@
 
 /**
  * @author daniel
- *
+ * 
  */
+
+/** Small thread for running vision code concurrently with debug server. */
 public class TestClient extends Thread {
+	
+	/** Constructor to set up new thread. */
 	public TestClient() {
 		super("Test Client");
 		start();
 	}
+	
+	/** Simple thread, runs the vision code. */
 	public void run() {
 		String[] args = {};
 		VisionTuner.main(args);
diff --git a/971CV/src/org/frc971/TestImageGetter.java b/971CV/src/org/frc971/TestImageGetter.java
index b0cf9b0..a61ed60 100644
--- a/971CV/src/org/frc971/TestImageGetter.java
+++ b/971CV/src/org/frc971/TestImageGetter.java
@@ -8,8 +8,6 @@
  *
  */
 
-//get debug images for Java camera processor
-
 import java.io.File;
 import java.io.IOException;
 
@@ -19,12 +17,16 @@
 
 import edu.wpi.first.wpijavacv.WPIColorImage;
 
+/** Get debug images for Java camera processor. */
 public class TestImageGetter {
 	
 	private String path_to_images;
 	
 	private final static Logger LOG = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
 	
+	/** The names of our debugging images, without paths.
+	 * The GetNext method should be used to get the first
+	 * image, and not GetCurrent. */
 	final static String[] images = {"45in_DoubleGreen.jpg",
 									"57inLargeTarget_DoubleGreenBK.jpg",
 									"FullField_DoubleGreenBK3.jpg",
@@ -49,15 +51,26 @@
 	
 	private WPIColorImage current_image = null;
 	
+	/** Helper method to concatenate paths, similar to Python's os.path.join(). */
 	private String cocatenate_paths(String path1, String path2) {
 		if (path1.charAt(path1.length() - 1) == '/')
 			return path1 + path2;
 		else
 			return path1 + "/" + path2;
 	}
+	
+	/** Constructor
+	 * 
+	 * @param path_to_images is the path to the directory where our images are.
+	 */
 	public TestImageGetter(String path_to_images) {
 		this.path_to_images = path_to_images;
 	}
+	
+	/** Gets the next debugging image.
+	 * 
+	 * @return Returns a WPIColorImage.
+	 */
 	public WPIColorImage GetNext() {
 		image_index++;
 		if (image_index < images.length) {
@@ -75,6 +88,11 @@
 			image_index--;
 			return null;
 	}
+	
+	/** Gets the previous debugging image.
+	 * 
+	 * @return Returns a WPIColorImage.
+	 */
 	public WPIColorImage GetPrev() {
 		image_index--;
 		if (image_index >= 0) {
@@ -92,6 +110,11 @@
 			image_index++;
 			return null;
 	}
+	
+	/** Gets the current debugging image. This is vestigial, it is not longer used.
+	 * 
+	 * @return Returns a WPIColorImage
+	 */
 	public WPIColorImage GetCurrent() {
 		return current_image;
 	}
diff --git a/971CV/src/org/frc971/TimeFormatter.java b/971CV/src/org/frc971/TimeFormatter.java
index 9f46bd4..1bff96f 100644
--- a/971CV/src/org/frc971/TimeFormatter.java
+++ b/971CV/src/org/frc971/TimeFormatter.java
@@ -9,12 +9,20 @@
 
 /**
  * @author daniel
- * Formats log messages with adequate timestamp
+ * 
  */
+
+/** Formats log messages with adequate timestamp. */
 public class TimeFormatter extends Formatter{
+	
+	/** Constructor, see Formatter. */
 	public TimeFormatter() {
 		super();
 	}
+	
+	/** Format a message in the propper way.
+	 * @return Includes time, name of logger, level and message.
+	 */
 	public String format(LogRecord message) {
 		//we need to include the date and time in our message
 		StringBuffer out = new StringBuffer();
diff --git a/971CV/src/org/frc971/VisionTuner.java b/971CV/src/org/frc971/VisionTuner.java
index a6f08c2..4aba589 100644
--- a/971CV/src/org/frc971/VisionTuner.java
+++ b/971CV/src/org/frc971/VisionTuner.java
@@ -9,6 +9,7 @@
 import java.util.logging.Logger;

 

 import java.io.FileNotFoundException;

+import java.io.IOException;

 

 import javax.swing.JPanel;

 import javax.swing.JSlider;

@@ -54,6 +55,8 @@
     private final JSlider hueMaxSlider = new JSlider();

     private final JSlider satMinSlider = new JSlider();

     private final JSlider valMinSlider = new JSlider();

+    

+    private ResultSender sender;

 

     private int totalFrames = -1; // don't count the first (warmup) frame

     private double totalMsec;

@@ -77,6 +80,13 @@
         	System.err.println("Warning: Logging initialization failed.");

         }

         

+        //initialize result sender

+        try {

+        	sender = new ResultSender();

+        }

+        catch (IOException e) {

+        	LOG.severe("Server initialization failed: " + e.getMessage() + " Result reporting disabled.");

+        }

         cameraFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

 

         recognizer.showIntermediateStages(true);

@@ -155,6 +165,9 @@
             LOG.fine("The recognizer took " + milliseconds + " ms, " + 

             (1000 * totalFrames / totalMsec) + " fps, %.2f avg");

         }

+        

+        //send results to atom. (and any connected clients)

+        

     }

 

     private void previousImage() {

@@ -202,13 +215,18 @@
         		LOG.severe("Cannot find test images.");

         }

         else {

-        	HTTPClient client = new HTTPClient();

-        	for (;;) {

-        		ImageWithTimestamp to_process = client.GetFrame();

-        		if (to_process.image != null) {

-        			tuner.processImage(to_process.image);

-        			LOG.fine("Captured time: " + Double.toString(to_process.timestamp));

-        		}

+        	try {

+        		HTTPClient client = new HTTPClient();

+        		for (;;) {

+            		ImageWithTimestamp to_process = client.GetFrame();

+            		if (to_process.image != null) {

+            			tuner.processImage(to_process.image);

+            			LOG.fine("Captured time: " + Double.toString(to_process.timestamp));

+            		}

+            	}

+        	}

+        	catch (IOException e) {

+        		LOG.severe("Client initialization failed.");

         	}

         }

     }

diff --git a/971CV/src/org/frc971/private_aos_camera_jar.jar b/971CV/src/org/frc971/private_aos_camera_jar.jar
index a1b8467..c202fe8 100644
--- a/971CV/src/org/frc971/private_aos_camera_jar.jar
+++ b/971CV/src/org/frc971/private_aos_camera_jar.jar
Binary files differ