Back to project page simple-dash.
The source code is released under:
MIT License
If you think the Android project simple-dash listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package org.bmdtech.simpledash.telemetry; /*from w ww. ja v a 2s . c o m*/ import android.os.Handler; import android.os.Message; import android.util.Log; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.bmdtech.simpledash.event.ConnectivityEvent; import org.bmdtech.simpledash.event.TelemetryEvent; import org.joda.time.DateTime; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static org.bmdtech.simpledash.event.ConnectivityEvent.Type.CLOSED; import static org.bmdtech.simpledash.event.ConnectivityEvent.Type.ERROR; import static org.bmdtech.simpledash.event.ConnectivityEvent.Type.OPEN; import static org.bmdtech.simpledash.event.ConnectivityEvent.Type.RETRY; import static org.bmdtech.simpledash.event.TelemetryEvent.CURRENT_GEAR_FIELD; import static org.bmdtech.simpledash.event.TelemetryEvent.RPM_FIELD; import static org.bmdtech.simpledash.event.TelemetryEvent.SPEED_FIELD; import static org.bmdtech.simpledash.event.TelemetryEvent.TELEMETRY_EVENT_TYPE; import static org.bmdtech.simpledash.event.TelemetryEvent.TYPE_FIELD; import static org.bmdtech.simpledash.utils.Logging.logEvent; public final class TelemetryConnector { private static final String TAG = "TCP"; private final Handler androidMessageHandler; private final ObjectMapper objectMapper; private TelemetryClient telemetryClient; public TelemetryConnector(Handler androidMessageHandler) { this.androidMessageHandler = androidMessageHandler; this.objectMapper = new ObjectMapper(); } public void connect(String ipAddress, String port) { this.telemetryClient = new TelemetryClient(ipAddress, port); Log.d(TAG, "Connecting client"); telemetryClient.connect(); } public void disconnect() { if (telemetryClient != null) { Log.d(TAG, "Disconnecting client"); telemetryClient.disconnect(); } } private class TelemetryClient { private static final int CONNECTION_TIMEOUT = 1000; private static final int RESCHEDULE_CONNECTION_DELAY = 5000; private final ScheduledExecutorService executorService; private final String ipAddress; private final String port; private final Socket clientSocket; private Thread readerThread; private TelemetryClient(String ipAddress, String port) { this.ipAddress = ipAddress; this.port = port; this.clientSocket = new Socket(); this.executorService = Executors.newSingleThreadScheduledExecutor(); } public void connect() { readerThread = new Thread(new Runnable() { @Override public void run() { try { clientSocket.connect(new InetSocketAddress(ipAddress, Integer.valueOf(port)), CONNECTION_TIMEOUT); readData(); } catch (Exception e) { rescheduleConnection(ERROR); } } private void readData() { BufferedReader inFromServer = null; try { inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); while (true) { if (clientSocket.isConnected() && !clientSocket.isClosed()) { String message = inFromServer.readLine(); if (message == null) { Log.e(TAG, "Message is null, rescheduling connection"); rescheduleConnection(CLOSED); break; } handleMessage(message); } else { break; } } } catch (Exception e) { Log.e(TAG, "Error while reading data from server.", e); rescheduleConnection(CLOSED); } finally { try { if (inFromServer != null) { inFromServer.close(); } } catch (Exception e) { // ignore } } } }); readerThread.start(); } public void disconnect() { executorService.shutdownNow(); try { clientSocket.close(); } catch (Exception e) { // ignore, system is shutting down and the client could have been already disconnected } } private void handleMessage(String message) { try { TypeReference<HashMap<String, String>> mapTypeReference = new TypeReference<HashMap<String, String>>() { }; Map<String, String> jsonValueMap = objectMapper.readValue(message, mapTypeReference); if (jsonValueMap.containsKey(TYPE_FIELD)) { switch (jsonValueMap.get(TYPE_FIELD)) { case TELEMETRY_EVENT_TYPE: TelemetryEvent event = new TelemetryEvent(new DateTime(Long.valueOf(jsonValueMap.get(TelemetryEvent.EVENT_TIMESTAMP_FIELD))), Integer.valueOf(jsonValueMap.get(CURRENT_GEAR_FIELD)), Float.valueOf(jsonValueMap.get(SPEED_FIELD)), Float.valueOf(jsonValueMap.get(RPM_FIELD))); logEvent(TAG, event); Message.obtain(androidMessageHandler, 0, event).sendToTarget(); break; case "ConnectionClosed": rescheduleConnection(CLOSED); break; case "ConnectionOpen": handleConnectionOpen(); break; default: Log.w(TAG, String.format("Received event with type <%s> which is currently unrecognized", jsonValueMap.get(TYPE_FIELD))); break; } } else { Log.w(TAG, String.format("Received JSON message <%s>, but it did not have a type identifier", message)); } } catch (Exception e) { Log.e(TAG, String.format("Error while trying to parse message <%s>. Not a valid JSON?", message), e); } } private void handleConnectionOpen() { Log.d(TAG, "Server connection successful!"); Message.obtain(androidMessageHandler, 0, new ConnectivityEvent(OPEN)).sendToTarget(); } private void rescheduleConnection(ConnectivityEvent.Type type) { // for some reason Android does not garbage collect the WebSocket client class // after an activity has been paused -> resumed. This can result in a "rogue" // thread that sill receives events and updates the UI if (!executorService.isTerminated()) { Message.obtain(androidMessageHandler, 0, new ConnectivityEvent(type)).sendToTarget(); executorService.schedule(new ReconnectionTask(ipAddress, port), RESCHEDULE_CONNECTION_DELAY, TimeUnit.MILLISECONDS); } } } private class ReconnectionTask implements Runnable { private final String ipAddress; private final String port; private ReconnectionTask(String ipAddress, String port) { this.ipAddress = ipAddress; this.port = port; } @Override public void run() { Message.obtain(androidMessageHandler, 0, new ConnectivityEvent(RETRY)).sendToTarget(); disconnect(); connect(ipAddress, port); } } }