Java tutorial
package edu.ucsb.cs.capstone.letmypeoplecode.smartrover; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.newdawn.slick.util.pathfinding.Path; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Set; import edu.ucsb.cs.capstone.letmypeoplecode.smartrover.astarsearch.LabMap; import edu.ucsb.cs.capstone.letmypeoplecode.smartrover.astarsearch.RoverMover; import edu.ucsb.cs.capstone.letmypeoplecode.smartrover.localization.LocalizerError; import edu.ucsb.cs.capstone.letmypeoplecode.smartrover.localization.Point3D; /** * Copyright (c) 2014 Let My People Code (UCSB Capstone) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) 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 Let My People Code (UCSB Capstone)? nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. 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. */ public class HttpServerTranslator { HttpParams httpParameters = new BasicHttpParams(); int timeoutSocket = 5000; int timeoutConnection = 3000; DefaultHttpClient httpClient = new DefaultHttpClient(httpParameters); private RoverController myRover; private boolean useSockets = false; private boolean UDP = true; public static String socketURL; private Socket socket; private BufferedWriter socketOut; private BufferedReader socketIn; private boolean networkActive = false; private static String TAG = "network_stuff"; private boolean enabled; //In case we don't want this interrupting something private boolean notifyTrainDone; private Runnable trainDoneCallback; private int sampleProgress; private boolean trainingInProgress; private MainActivity mainActivity; private Thread updateThread = new Thread(new Runnable() { @Override public void run() { while (true) { try { while (enabled) { try { parseJson(); networkActive = true; Thread.sleep(25); } catch (InterruptedException e) { Log.w(TAG, e); } catch (JSONException e) { Log.e(TAG, "Bad json", e); } } } catch (IOException e) { Log.e(TAG, "Network error", e); netfail(); } try { Thread.sleep(500); //Stop spam (this is the interval to reconnect after failure) } catch (InterruptedException ass) { } } } }); private Thread updateThreadSocket = new Thread(new Runnable() { @Override public void run() { while (true) { try { socket = new Socket(socketURL, 5001); socket.setSoTimeout(7000); socketOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); socketIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); while (enabled) { try { parseJson(); networkActive = true; } catch (JSONException e) { Log.w(TAG, e); } } } catch (UnknownHostException e) { Log.w(TAG, e); } catch (IOException e) { Log.w(TAG, e); } finally { netfail(); try { if (socket != null) socket.close(); } catch (IOException e) { } } } } }); /** * Send structure: * float x_pos * float y_pos * float heading * int trainProgress * * * Receive structure: * uint8_t command [:none,:forward,:reverse,:turn_left,:turn_right,:train] * float train_x * float train_y * */ private Thread updateThreadUDP = new Thread(new Runnable() { @Override public void run() { int sendSize = 4 + 4 + 4 + 4; int recvSize = 1 + 4 + 4; ByteBuffer sendBuf = ByteBuffer.allocate(sendSize); ByteBuffer recvBuf = ByteBuffer.allocate(recvSize); byte[] recv = new byte[recvSize]; byte[] send = new byte[sendSize]; DatagramSocket udp_socket = null; String currentURL = ""; DatagramPacket sendPacket = new DatagramPacket(send, send.length); DatagramPacket recvPacket = new DatagramPacket(recv, recv.length); trainingInProgress = false; while (true) { if (!enabled) continue; try { if (!currentURL.equals(socketURL)) { //This means the URL has changed in settings, set up the socket again if (udp_socket != null && udp_socket.isConnected()) { udp_socket.disconnect(); udp_socket.close(); } udp_socket = new DatagramSocket(); udp_socket.setSoTimeout(200); udp_socket.setReuseAddress(true); udp_socket.connect(new InetSocketAddress(InetAddress.getByName(socketURL), 5001)); currentURL = socketURL; } while (enabled && udp_socket != null) { Thread.sleep(16); sendBuf.clear(); recvBuf.clear(); sendBuf.putFloat((float) myRover.getX()); sendBuf.putFloat((float) myRover.getY()); sendBuf.putFloat((float) myRover.getHeadingDegrees()); sendBuf.putInt(sampleProgress); sendPacket.setData(sendBuf.array()); udp_socket.send(sendPacket); udp_socket.receive(recvPacket); recvBuf.put(recvPacket.getData()); float train_x = recvBuf.getFloat(1); float train_y = recvBuf.getFloat(5); switch (recvBuf.get(0)) { case 1: myRover.forwardBackward(1); break; case 2: myRover.forwardBackward(-1); break; case 3: myRover.turn(0.8); break; case 4: myRover.turn(-0.8); break; case 5: if (trainingInProgress) break; final float train_x_param = train_x; final float train_y_param = train_y; new Thread(new Runnable() { @Override public void run() { try { trainingInProgress = true; sampleProgress = 0; myRover.bluetoothTrain(new Point3D(train_x_param, train_y_param)); trainingInProgress = false; } catch (LocalizerError e) { Log.e(TAG, "Network train error", e); } } }).start(); break; default: myRover.stop(); break; } networkActive = true; // Log.i(TAG,String.format("Recv %d",recvPacket.getData()[0])); } } catch (SocketException e) { // Log.w(TAG,e); } catch (UnknownHostException e) { Log.w(TAG, e); } catch (IOException e) { Log.w(TAG, e); } catch (BufferOverflowException e) { Log.w(TAG, e); //This happens if the received packet is the wrong size } catch (InterruptedException e) { } finally { netfail(); } } } }); public HttpServerTranslator(Context context) { SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); if (context instanceof MainActivity) { this.mainActivity = (MainActivity) context; } socketURL = pref.getString(SettingsActivity.SOCKET_URL, ""); enabled = true; notifyTrainDone = false; HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); } public void setRover(RoverController r) { myRover = r; myRover.onTrainProgressChanged(new ProgressCallback<Integer>() { @Override public void run(Integer param) { sampleProgress = param; } }); } public void startUpdating() { if (myRover == null) throw new IllegalArgumentException("You need to set a rover controller first"); if (useSockets) { if (UDP) { updateThreadUDP.setPriority(Thread.MAX_PRIORITY); updateThreadUDP.start(); } else { updateThreadSocket.setPriority(Thread.MAX_PRIORITY); updateThreadSocket.start(); } } else { updateThread.setPriority(Thread.MAX_PRIORITY); updateThread.start(); } } public void setEnabled(boolean en) { enabled = en; } public boolean isNetworkActive() { return networkActive; } public void onTrainDone(Runnable r) { trainDoneCallback = r; } private void parseJson() throws IOException, JSONException { BufferedReader rd; String getParams = "&xPosition=" + myRover.getLastValidX() + "&yPosition=" + myRover.getLastValidY() + "°rees=" + myRover.getHeadingDegrees(); if (this.mainActivity != null) { if (this.mainActivity.getStreamVideoSwitch().isChecked()) { getParams += "&video_url=" + this.mainActivity.getStreamVideoAddress().getText(); } } if (notifyTrainDone) { getParams += "&trainDone"; notifyTrainDone = false; } if (!useSockets) { String requestURL = "http://www.letmypeoplecode.com/rover?key=letmypeoplecode" + getParams; HttpGet request = new HttpGet(requestURL); HttpResponse response = httpClient.execute(request); rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); } else { rd = socketIn; socketOut.write(getParams + "\n"); socketOut.flush(); } // long t0 = System.nanoTime(); String line = ""; StringBuilder str = new StringBuilder(); while ((line = rd.readLine()) != null) { if (useSockets && line.equals("f")) break; str.append(line); } String s = str.toString(); s = s.replaceAll("\n", "\\n"); JSONObject json; String message; try { json = new JSONObject(s); message = json.getString("messages"); } catch (JSONException e) { e.printStackTrace(); return; } if (!enabled) return; if (message.equals("yes")) { JSONArray actions = json.getJSONArray("actions"); for (int i = 0; i < actions.length(); i++) { String action = (String) actions.get(i); if (action.equals("forward")) { myRover.forward(); } if (action.equals("reverse")) { myRover.reversePanic(); } if (action.equals("turn_right")) { myRover.turnRight(); } if (action.equals("turn_left")) { myRover.turnLeft(); } if (action.equals("fork_up")) { myRover.forkUp(); } if (action.equals("fork_down")) myRover.forkDown(); if (action.equals("mirror_left")) { myRover.mirrorLeft(); } if (action.equals("mirror_right")) { myRover.mirrorRight(); } if (action.startsWith("move_x")) { String[] parts = action.split(" "); double x = Double.parseDouble(parts[1]); double y = Double.parseDouble(parts[2]); //Get the destination int go_xInInches = RoverController.convertFeetToInchesWithRounding(x); int go_yInInches = RoverController.convertFeetToInchesWithRounding(y); //Get where the rover currently is int currentXInInches = RoverController.convertFeetToInchesWithRounding(myRover.getLastValidX()); int currentYInInches = RoverController.convertFeetToInchesWithRounding(myRover.getLastValidY()); // Safety check, make sure we aren't trying to go off the map if ((go_xInInches / LabMap.GRID_SIZE_IN_INCHES >= myRover.getMap().getWidthInTiles()) || go_yInInches / LabMap.GRID_SIZE_IN_INCHES >= myRover.getMap().getHeightInTiles()) { Log.i("position", "Out of bounds"); return; } if (myRover.getCurrentStep() != 0) { Log.i("position", "Cannot select new destination until old destination has been reached."); return; } // The formatting here is to make it readable since the parameters are long // We are calculating a new path and setting it in the rover for the navigation to handle myRover.setPath(myRover.getFinder().findPath(new RoverMover(LabMap.ROVER), currentXInInches / LabMap.GRID_SIZE_IN_INCHES, currentYInInches / LabMap.GRID_SIZE_IN_INCHES, go_xInInches / LabMap.GRID_SIZE_IN_INCHES, go_yInInches / LabMap.GRID_SIZE_IN_INCHES)); // Force the first step because the system won't do anything otherwise if (myRover.getPath() != null) { Path.Step step = myRover.getPath().getStep(myRover.getCurrentStep()); myRover.setCurrentStep(myRover.getCurrentStep() + 1); // Convert to feet double newX = (step.getX() * LabMap.GRID_SIZE_IN_INCHES) / 12D; double newY = (step.getY() * LabMap.GRID_SIZE_IN_INCHES) / 12D; myRover.setDesiredX(newX); myRover.setDesiredY(newY); Log.i("position", "newStep: " + step.getX() + ", " + step.getY()); } } if (action.startsWith("train")) { String[] parts = action.split(" "); try { myRover.bluetoothTrain(new Point3D(parts[1], parts[2])); notifyTrainDone = true; if (trainDoneCallback != null) trainDoneCallback.run(); } catch (LocalizerError e) { Log.w(TAG, e); //TODO: Perhaps notify the web UI of an error? } } if (action.equals("request_video_url")) { if (this.mainActivity != null) { // Flip the switch this.mainActivity.runOnUiThread(new Runnable() { @Override public void run() { HttpServerTranslator.this.mainActivity.getStreamVideoSwitch().setChecked( HttpServerTranslator.this.mainActivity.getStreamVideoSwitch().isChecked()); } }); } } } } else { myRover.stop(); } // Log.i("net_speed",String.format("%.4f",(System.nanoTime()-t0)/1.0E9)); } //Something with the connection fucked up, clean things private void netfail() { networkActive = false; if (myRover != null) myRover.stop(); } }