ukhas.Habitat_interface.java Source code

Java tutorial

Introduction

Here is the source code for ukhas.Habitat_interface.java

Source

// Copyright 2012 (C) Matthew Brejza
//
// 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 3 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.

package ukhas;

import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Queue;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.commons.codec.binary.Base64;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;

import net.sf.json.*;

import com.fourspaces.couchdb.CouchResponse;
import com.fourspaces.couchdb.Database;
import com.fourspaces.couchdb.Document;
import com.fourspaces.couchdb.Session;
import com.fourspaces.couchdb.View;
import com.fourspaces.couchdb.ViewResults;

public class Habitat_interface {

    private String _habitat_url = "habitat.habhub.org";
    private String _habitat_db = "habitat";
    private Listener _listener_info;
    private String _listener_telem_UUID = "";
    private String _listener_info_UUID = "";

    protected ArrayList<HabitatRxEvent> _listeners = new ArrayList<HabitatRxEvent>();

    public ConcurrentHashMap<String, String> payload_configs = new ConcurrentHashMap<String, String>();
    public ConcurrentHashMap<String, String> flight_configs = new ConcurrentHashMap<String, String>();
    private ConcurrentHashMap<String, TelemetryConfig> telem_configs = new ConcurrentHashMap<String, TelemetryConfig>();

    private boolean _newTelemConfigs = false;

    private Session s;
    private Database db;
    private Thread sdThread;

    public String device = "device";//"XOOM";
    public String device_software = "";//"4.0.x";
    public String application = "HAB Modem";
    public String application_version = "pre-alpha";

    private int _prev_query_time = 5 * 60 * 60;

    private boolean _queried_current_flights = false;

    //TODO: if failed due to connection error, identify error and dont clear the list.

    private Queue<Telemetry_string> out_buff = new LinkedBlockingQueue<Telemetry_string>();
    private Queue<QueueItem> _operations = new LinkedBlockingQueue<QueueItem>();

    public Habitat_interface(String callsign) {
        _listener_info = new Listener(callsign, false);
    }

    public Habitat_interface(String habitat_url, String habitat_db, Listener listener_info) {
        _habitat_url = habitat_url;
        _habitat_db = habitat_db;
        _listener_info = listener_info;

    }

    public Habitat_interface(String habitat_url, String habitat_db) {
        _habitat_url = habitat_url;
        _habitat_db = habitat_db;

    }

    public void Test() {
        s = new Session(_habitat_url, 80);
        db = s.getDatabase(_habitat_db);
        List<Document> foodoc;
        View v = new View("payload_configuration/callsign_time_created_index");
        //View v = new View("payload_configuration/callsign_time_created_index&startkey%3D[%22APEX%22]");
        // v.setKey("startkey=APEX");
        v.setStartKey("[%22APEX%22]");
        v.setLimit(1);
        //foodoc = db.view("flight/end_start_including_payloads").getResults();
        foodoc = db.view(v).getResults();
        foodoc.toString();
    }

    public void addHabitatRecievedListener(HabitatRxEvent listener) {
        _listeners.add(listener);
    }

    public void addDataFetchTask(String callsign, long startTime, long stopTime, int limit) {
        _operations.offer(new QueueItem(1, callsign, startTime, stopTime, limit));
        StartThread();
    }

    public void updateChaseCar(Listener newlocation) {
        _listener_info = newlocation;
        _operations.offer(new QueueItem(3, 0));
        StartThread();
    }

    public void updateListener(Listener newlistener) {
        _listener_info = newlistener;
    }

    public void addGetActiveFlightsTask() {
        _operations.offer(new QueueItem(2, 0));
        StartThread();
    }

    private String resolvePayloadID(String callsign) {
        String c = callsign.toUpperCase();
        if (payload_configs.containsKey(c))
            return payload_configs.get(c);
        else if (!_queried_current_flights)
            _queried_current_flights = queryActiveFlights();

        if (payload_configs.containsKey(c))
            return payload_configs.get(c);
        else {
            queryAllPayloadDocs(c);
            if (payload_configs.containsKey(c))
                return payload_configs.get(c);
            else
                return null; //TODO: add list of non payloads
        }
    }

    public boolean queryAllPayloadDocs(String callsign) {
        String docid = null;
        try {
            //open DB connection
            if (s == null) {
                s = new Session(_habitat_url, 80);
                db = s.getDatabase(_habitat_db);// + "/_design/payload_telemetry/_update/add_listener");
            } else if (db == null)
                db = s.getDatabase(_habitat_db);

            List<Document> docsout;
            View v = new View("payload_configuration/callsign_time_created_index");
            //startkey=["CRAAG1",1357396479]&endkey=["CRAAG1",0]&descending=true
            v.setStartKey("[%22" + callsign.toUpperCase() + "%22,"
                    + Long.toString((System.currentTimeMillis() / 1000L)) + "]");
            v.setEndKey("[%22" + callsign.toLowerCase() + "%22,0]");
            //v.setWithDocs(true);
            v.setLimit(1);
            v.setDescending(true);
            ViewResults r = db.view(v);
            docsout = r.getResults();

            //docsout.toString();

            //docsout.get(0).getJSONObject().getJSONObject("doc").getString("type")
            //docsout.get(1).getJSONObject().getJSONObject("doc").getJSONArray("sentences").getJSONObject(0).getString("callsign")

            if (docsout.size() > 0) {
                JSONArray ja = docsout.get(0).getJSONArray("key");
                String ss = docsout.get(0).getId();
                if (ja.getString(0).equals(callsign))
                    docid = ss;
            }

        } catch (Exception e) {
            System.out.println("ERROR: " + e.toString());
            return false;
        }

        if (docid != null)
            payload_configs.put(callsign.toUpperCase(), docid);

        return true;

    }

    private boolean queryPayloadConfig(String docID) {
        try {
            //open DB connection
            if (s == null) {
                s = new Session(_habitat_url, 80);
                db = s.getDatabase(_habitat_db);// + "/_design/payload_telemetry/_update/add_listener");
            } else if (db == null)
                db = s.getDatabase(_habitat_db);

            Document doc = db.getDocument(docID);

            if (doc == null)
                return false;
            CouchResponse cr = s.getLastResponse();
            if (!cr.isOk())
                return false;

            JSONObject obj;
            JSONArray sentences;
            JSONArray fields;
            JSONArray filters;

            TelemetryConfig tc = new TelemetryConfig();

            obj = doc.getJSONObject();
            if (obj.containsKey("sentences")) {
                sentences = obj.getJSONArray("sentences");
                for (int i = 0; i < sentences.size(); i++) {
                    String call = "";
                    if (sentences.getJSONObject(i).containsKey("callsign")) {
                        call = sentences.getJSONObject(i).getString("callsign");
                    } else
                        return false;

                    if (sentences.getJSONObject(i).containsKey("fields")) {
                        fields = sentences.getJSONObject(i).getJSONArray("fields");
                        for (int j = 0; j < fields.size(); j++) {
                            String name = fields.getJSONObject(j).getString("name");
                            String sensor = "";
                            String format = "";
                            TelemetryConfig.DataType dt = TelemetryConfig.DataType.IGNORE;
                            if (fields.getJSONObject(j).containsKey("sensor"))
                                sensor = fields.getJSONObject(j).getString("sensor");
                            if (fields.getJSONObject(j).containsKey("format"))
                                format = fields.getJSONObject(j).getString("format");

                            if (name.equals("sentence_id")) {
                            } else if (name.equals("time")) {
                            } else if (name.equals("latitude")) {
                                if (sensor.equals("base.ascii_int")) {
                                    //TODO
                                    tc.gpsFormat = TelemetryConfig.GPSFormat.INT;
                                }
                            } else if (name.equals("longitude")) {
                                if (sensor.equals("base.ascii_int")) {
                                    //TODO
                                    tc.gpsFormat = TelemetryConfig.GPSFormat.INT;
                                }
                            } else if (name.equals("altitude")) {
                            } else {
                                if (sensor.equals("base.ascii_int")) {
                                    dt = TelemetryConfig.DataType.INT;
                                } else if (sensor.equals("base.string")) {
                                    dt = TelemetryConfig.DataType.STRING;
                                } else if (sensor.equals("base.ascii_float")) {
                                    dt = TelemetryConfig.DataType.FLOAT;
                                }
                            }

                            tc.addField(name, dt);
                        }

                        if (sentences.getJSONObject(i).containsKey("filters")) {
                            if (sentences.getJSONObject(i).getJSONObject("filters").containsKey("post")) {
                                filters = sentences.getJSONObject(i).getJSONObject("filters").getJSONArray("post");
                                for (int j = 0; j < filters.size(); j++) {
                                    String filtertype = "";
                                    String source = "";
                                    String factor = "";
                                    String offset = "";
                                    String round = "";
                                    String type = "";

                                    if (filters.getJSONObject(j).containsKey("filter"))
                                        filtertype = filters.getJSONObject(j).getString("filter");
                                    if (filters.getJSONObject(j).containsKey("source"))
                                        source = filters.getJSONObject(j).getString("source");
                                    if (filters.getJSONObject(j).containsKey("factor"))
                                        factor = filters.getJSONObject(j).getString("factor");
                                    if (filters.getJSONObject(j).containsKey("type"))
                                        type = filters.getJSONObject(j).getString("type");
                                    if (filters.getJSONObject(j).containsKey("offset"))
                                        offset = filters.getJSONObject(j).getString("offset");
                                    if (filters.getJSONObject(j).containsKey("round"))
                                        round = filters.getJSONObject(j).getString("round");

                                    if (filtertype.equals("common.numeric_scale")) {
                                        tc.addFilter(source, factor, offset, round);
                                    } else {

                                    }

                                }
                            }
                        }

                    }
                    //save tc to some sort of data structure
                    telem_configs.put(call.toUpperCase(), tc);
                    _newTelemConfigs = true;
                }
            }
        } catch (Exception e) {
            System.out.println("ERROR: " + e.toString());
            return false;
        }

        return true;
    }

    private boolean queryActiveFlights() {
        try {
            //open DB connection
            if (s == null) {
                s = new Session(_habitat_url, 80);
                db = s.getDatabase(_habitat_db);// + "/_design/payload_telemetry/_update/add_listener");
            } else if (db == null)
                db = s.getDatabase(_habitat_db);

            List<Document> docsout;
            View v = new View("flight/end_start_including_payloads");

            v.setStartKey("[" + Long.toString((System.currentTimeMillis() / 1000L) - (2 * 60 * 60)) + "]");

            v.setWithDocs(true);
            v.setLimit(30);

            ViewResults r = db.view(v);
            docsout = r.getResults();

            //docsout.toString();

            //docsout.get(0).getJSONObject().getJSONObject("doc").getString("type")
            //docsout.get(1).getJSONObject().getJSONObject("doc").getJSONArray("sentences").getJSONObject(0).getString("callsign")

            for (int i = 0; i < docsout.size(); i++) {

                JSONObject obj;

                obj = docsout.get(i).getJSONObject().getJSONObject("doc");
                if (obj != null) {
                    if (obj.containsKey("type")) {
                        if (obj.getString("type").equals("payload_configuration")) {
                            if (obj.containsKey("sentences")) {
                                JSONArray jar = obj.getJSONArray("sentences");
                                for (int j = 0; j < jar.size(); j++) {
                                    if (jar.getJSONObject(j).containsKey("callsign")) {
                                        String call = jar.getJSONObject(j).getString("callsign");
                                        if (!flight_configs.containsKey(call.toUpperCase()))
                                            flight_configs.put(call.toUpperCase(), docsout.get(i).getId());
                                        if (obj.containsKey("_id"))
                                            payload_configs.put(call.toUpperCase(), obj.getString("_id"));
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    //TODO: if flight doc exists, query that view instead
    private boolean getPayloadDataSince(long timestampStart, long timestampStop, int limit, String payloadID,
            String callsign) {
        double prevAltitude = -99999999;
        double maxAltitude = -99999999;
        long prevtime = -1;
        try {

            //open DB connection
            if (s == null) {
                s = new Session(_habitat_url, 80);
                db = s.getDatabase(_habitat_db);// + "/_design/payload_telemetry/_update/add_listener");
            } else if (db == null)
                db = s.getDatabase(_habitat_db);

            View v = new View("payload_telemetry/payload_time");

            long starttime;
            if (timestampStart > 0)
                starttime = timestampStart;
            else
                starttime = (System.currentTimeMillis() / 1000L) - _prev_query_time;

            v.setStartKey("[%22" + payloadID + "%22," + Long.toString(starttime) + "]");
            v.setEndKey("[%22" + payloadID + "%22," + Long.toString(timestampStop) + "]");

            v.setWithDocs(true);
            v.setLimit(limit);

            System.out.println("DEBUG: DATABASE GOT QUERY");

            TreeMap<Long, Telemetry_string> out = new TreeMap<Long, Telemetry_string>();

            JsonFactory fac = new JsonFactory();
            InputStream is = db.view_dont_parse(v);
            long lasttime = 0;

            if (is != null) {

                JsonParser jp = fac.createJsonParser(is);

                String str, str1;
                boolean gotkey = false;
                boolean keypt1 = false;

                TelemetryConfig tc = null;
                if (telem_configs.containsKey(callsign)) {
                    tc = telem_configs.get(callsign);
                }

                while (jp.nextToken() != null)// || (jp.getCurrentLocation().getCharOffset() < body.length()-50)) && nullcount < 20) //100000 > out.size())
                {
                    //jp.nextToken();

                    str = jp.getCurrentName();
                    str1 = jp.getText();
                    if (str == "key" && str1 == "[")
                        gotkey = true;
                    else if (gotkey) {
                        keypt1 = true;
                        gotkey = false;
                    } else if (keypt1) {
                        keypt1 = false;
                        try {
                            lasttime = Long.parseLong(str1);
                        } catch (NumberFormatException e) {
                            System.out.println("ERROR PARSING - NUMBER FORMAT EXCEPTION!!!");
                        }

                    }
                    if (str != null && str1 != null) {
                        if (str.equals("_sentence") && !str1.equals("_sentence")) {
                            Telemetry_string ts = new Telemetry_string(str1, lasttime, tc);
                            if (!ts.isZeroGPS() && ts.time != null) {
                                if (out.size() > 0) {
                                    if (out.lastEntry().getValue().coords.alt_valid) {
                                        prevAltitude = out.lastEntry().getValue().coords.altitude;
                                        prevtime = out.lastEntry().getValue().time.getTime();
                                    }
                                }
                                out.put(new Long(ts.time.getTime()), ts);
                                if (ts.coords.alt_valid)
                                    maxAltitude = Math.max(ts.coords.altitude, maxAltitude);
                            }
                        }

                        //nullcount = 0;
                    }
                    //else
                    //   nullcount++;
                }
                jp.close();
                is.close();

            }
            s.clearCouchResponse();

            System.out.println("DEBUG: DATABASE PROCESSING DONE");

            AscentRate as = new AscentRate();

            if (out.size() >= 2 && prevAltitude > -9999) {
                as = new AscentRate();
                as.addData(prevtime, prevAltitude);
                if (out.lastEntry().getValue().coords.alt_valid) {
                    as.addData(out.lastEntry().getValue().time.getTime(),
                            out.lastEntry().getValue().coords.altitude);

                } else
                    as = new AscentRate();
            }
            lasttime += 1000;
            if (lasttime >= timestampStart && lasttime <= timestampStop)
                fireDataReceived(out, true, callsign, timestampStart, lasttime, as, maxAltitude);
            else
                fireDataReceived(out, true, callsign, timestampStart, timestampStop, as, maxAltitude);

            return true;
        }

        catch (Exception e) {
            fireDataReceived(null, false, e.toString(), timestampStart, timestampStop, null, -99999999);
            return false;
        }
    }

    public void getActivePayloads() {

        try {
            //open DB connection
            if (s == null) {
                s = new Session(_habitat_url, 80);
                db = s.getDatabase(_habitat_db);// + "/_design/payload_telemetry/_update/add_listener");
            } else if (db == null)
                db = s.getDatabase(_habitat_db);

            List<Document> foodoc;
            View v = new View("payload_telemetry/time");

            v.setStartKey(Long.toString((System.currentTimeMillis() / 1000L) - _prev_query_time));
            v.setWithDocs(true);
            v.setLimit(40);

            ViewResults r = db.view(v);
            foodoc = r.getResults();
            // foodoc = db.view(v).getResults();
            foodoc.toString();
            //((JSONObject)((JSONObject)foodoc.get(1).getJSONObject().get("doc")).get("data")).get("payload")

        } catch (Exception e) {

        }
    }

    public void upload_payload_telem(Telemetry_string input) {
        boolean added = false;
        added = out_buff.offer(input);

        if (added) {
            _operations.offer(new QueueItem(0, 1));
            StartThread();
        }
    }

    private synchronized void StartThread() {
        if (sdThread == null) {
            sdThread = new SendThread();
            sdThread.start();
        } else if (!sdThread.isAlive()) {
            sdThread = new SendThread();
            sdThread.start();
        }
    }

    private boolean _update_chasecar() {
        try {
            //open DB connection
            if (s == null) {
                s = new Session(_habitat_url, 80);
                db = s.getDatabase(_habitat_db);// + "/_design/payload_telemetry/_update/add_listener");
            } else if (db == null)
                db = s.getDatabase(_habitat_db);

            if (_listener_info != null) {
                update_listener_telem();
                /*
                Document doc = new Document ();
                Document doc_info = new Document ();
                    
                //date uploaded
                Date time = new Date();
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
                String t = dateFormat.format(time);
                t = t.substring(0, t.length()-2) + ":" + t.substring(t.length()-2, t.length());
                    
                    
                doc.put("type", "listener_telemetry");
                doc.put("time_created", t);
                doc.put("time_uploaded", t);
                doc_info.put("type", "listener_information");
                doc_info.put("time_created", t);
                doc_info.put("time_uploaded", t);
                    
                JSONObject client = new JSONObject();
                JSONObject l_info = new JSONObject();
                client.put("device", device);
                client.put("device_software", device_software);
                client.put("application", application);
                client.put("application_version", application_version);
                l_info.put("callsign", _listener_info.CallSign());
                    
                JSONObject data = _listener_info.getJSONDataField();
                data.put("client", client);
                          
                doc.put("data", data);
                    
                doc_info.put("data",l_info);
                    
                String sha = _listener_info.toSha256();
                String sha_info = _listener_info.toSha256();
                    
                db.saveDocument(doc,sha);   
                CouchResponse cr = s.getLastResponse();
                System.out.println(cr);            
                if (cr.isOk())
                   _listener_telem_UUID = sha;      
                    
                db.saveDocument(doc_info,sha_info);   
                cr = s.getLastResponse();
                System.out.println(cr);            
                if (cr.isOk())
                   _listener_info_UUID = sha_info;   
                */
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private boolean _upload(Telemetry_string input) {
        try {
            //open DB connection
            if (s == null) {
                s = new Session(_habitat_url, 80);
                db = s.getDatabase(_habitat_db);// + "/_design/payload_telemetry/_update/add_listener");
            } else if (db == null)
                db = s.getDatabase(_habitat_db);

            if (_listener_info != null) {
                if (_listener_info.data_changed_info())
                    update_listener_info();

                if (_listener_info.data_changed_telem()) //upload listeners location
                {
                    update_listener_telem();
                    /*
                    Document doc = new Document ();
                        
                    //date uploaded
                    Date time = new Date();
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
                    String t = dateFormat.format(time);
                    t = t.substring(0, t.length()-2) + ":" + t.substring(t.length()-2, t.length());
                        
                        
                        
                        
                    doc.put("type", "listener_telemetry");
                    doc.put("time_created", t);
                    doc.put("time_uploaded", t);            
                        
                    JSONObject client = new JSONObject();
                    client.put("device", device);
                    client.put("device_software", device_software);
                    client.put("application", application);
                    client.put("application_version", application_version);
                        
                    JSONObject data = _listener_info.getJSONDataField();
                    data.put("client", client);
                              
                    doc.put("data", data);
                        
                        
                    String sha = _listener_info.toSha256(); 
                        
                    db.saveDocument(doc,sha);
                    CouchResponse cr = s.getLastResponse();
                    System.out.println(cr);
                    if (cr.isOk())
                       _listener_telem_UUID = sha;
                    */
                }
            }

            Document doc = new Document();
            JSONObject data = new JSONObject();
            JSONObject receivers = new JSONObject();
            JSONObject receiver = new JSONObject();

            //date uploaded
            Date time = new Date();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
            String t = dateFormat.format(time);
            t = t.substring(0, t.length() - 2) + ":" + t.substring(t.length() - 2, t.length());

            String str64 = input.raw_64_str();

            data.put("_raw", str64);
            receiver.put("time_created", input.doc_time_created);
            receiver.put("time_uploaded", t);
            if (input.habitat_metadata != null)
                receiver.putAll(input.habitat_metadata);
            if (_listener_telem_UUID != "")
                receiver.put("latest_listener_telemtry", _listener_telem_UUID);
            if (_listener_info_UUID != "")
                receiver.put("latest_listener_information", _listener_info_UUID);
            receivers.put(_listener_info.CallSign(), receiver);

            doc.put("type", "payload_telemetry");
            doc.put("data", data.toString());
            doc.put("receivers", receivers.toString());

            String sha = input.toSha256();

            //System.out.println(doc.toString());

            db.saveDocument(doc, sha); //try to upload as only listener
            CouchResponse cr = s.getLastResponse(); //see if successful

            if (cr.isOk()) { //addition went well, but get the payload config ID if not already
                if (payload_configs.containsKey(input.callsign))
                    return true;

                //if not already existing, query the now parsed document
                Document result = db.getDocument(sha);

                if (result != null) {
                    if (result.getJSONObject().containsKey("data")) {
                        JSONObject objdata = result.getJSONObject().getJSONObject("data");
                        if (objdata.containsKey("_parsed")) {
                            objdata = objdata.getJSONObject("_parsed");
                            if (objdata.containsKey("payload_configuration"))
                                payload_configs.put(input.callsign, objdata.getString("payload_configuration"));
                            if (objdata.containsKey("flight"))
                                flight_configs.put(input.callsign, objdata.getString("flight"));
                        }
                    }
                }
                return true;
            }

            if (!cr.getErrorId().equals("conflict")) {
                //throw error but continue
            }

            Document result = db.getDocument(sha);

            //if payload configs does not contain the payload config ID, get it
            if (!payload_configs.containsKey(input.callsign)) {

                if (result != null) {
                    if (result.getJSONObject().containsKey("data")) {
                        JSONObject objdata = (JSONObject) (result.getJSONObject().get("data"));
                        if (objdata.containsKey("_parsed")) {
                            JSONObject obparse = (JSONObject) objdata.get("_parsed");
                            if (obparse.containsKey("payload_configuration"))
                                payload_configs.put(input.callsign,
                                        obparse.get("payload_configuration").toString());
                        }
                    }
                }
            }

            int its = 0;
            //start of main loop
            while (its < 30) {
                its++;

                if (result == null)
                    return false; //somethings gone wrong

                //instead try to append document

                JSONObject existing_receivers = new JSONObject();

                JSONObject _dat_a = (JSONObject) result.get("data");
                if (_dat_a == null) {
                    System.out.println("DID NOT PARSE DATA SECTION");
                }
                /*
                double gf = 4.3600000000000003197;
                double ss = 0.0000000000000003197;
                double p = gf - 4.36;
                System.out.println(_dat_a.get("battery") + "   " + gf + "   " + ss + "   " + p); */
                if (!result.containsKey("receivers"))
                    return false; //somethings gone wrong

                existing_receivers = (JSONObject) result.remove("receivers");
                existing_receivers.put(_listener_info.CallSign(), receiver);
                result.put("receivers", existing_receivers);

                db.saveDocument(result, sha);
                cr = s.getLastResponse();
                //System.out.println(cr.statusCode);
                if (cr.isOk())
                    return true;
                if (cr.getErrorId() == null) {
                    //throw error but continue
                    its += 9;
                } else if (!cr.getErrorId().equals("conflict")) {
                    //throw error but continue
                    its += 9;
                }
                //get document for next run through
                result = db.getDocument(sha);
            }
            //System.out.println(cr.isOk() + "  " + cr.getErrorId() + "  " + cr.getErrorReason());
            //System.out.println(s.getLastResponse());

        } catch (Exception e) {
            return false;
        }
        return false;
    }

    protected void fireDataReceived(TreeMap<Long, Telemetry_string> out, boolean success, String callsign,
            long startTime, long endTime, AscentRate as, double maxAltitude) {
        for (int i = 0; i < _listeners.size(); i++) {
            _listeners.get(i).HabitatRx(out, success, callsign, startTime, endTime, as, maxAltitude);
        }
    }

    public void addStringRecievedListener(HabitatRxEvent listener) {
        _listeners.add(listener);
    }

    class SendThread extends Thread {

        public void run() {
            boolean buff_empty = false;
            while (!buff_empty) {

                if (!_operations.isEmpty()) {
                    QueueItem qi = _operations.poll();
                    if (qi != null) {
                        if (qi.type == 0) { //upload telem

                            Telemetry_string tosend = out_buff.peek();
                            boolean res = true;
                            if (tosend != null)
                                res = _upload(tosend); //now we have some telem, lets send it                       
                            if (!res)
                                System.out.println("UPLOAD FAILED :(");
                            else //success, remove data from queue
                                out_buff.poll();
                        } else if (qi.type == 1) { //get data
                            String id = resolvePayloadID(qi.callsign);
                            if (!telem_configs.containsKey(qi.callsign.toUpperCase()))
                                queryPayloadConfig(id);
                            if (id != null)
                                getPayloadDataSince(qi.startTime, qi.stopTime, qi.count, id, qi.callsign);
                        } else if (qi.type == 2) //update list of active flights
                        {
                            _queried_current_flights = queryActiveFlights();
                        } else //upload chasecar
                        {
                            _update_chasecar();
                        }
                    } else
                        buff_empty = true;
                } else
                    buff_empty = true;

            }

            //deal with any items in the send queue which are left over
            buff_empty = false;
            while (!buff_empty) {

                if (out_buff.isEmpty()) {
                    buff_empty = true;
                } else {
                    Telemetry_string tosend = out_buff.poll();

                    boolean res = true;
                    if (tosend == null)
                        buff_empty = true;
                    else
                        res = _upload(tosend); //now we have some telem, lets send it

                    if (!res)
                        System.out.println("UPLOAD FAILED :(");
                }
            }
        }
    }

    private boolean update_listener_telem() {
        try {
            Document doc = new Document();
            Document doc_info = new Document();

            //date uploaded
            Date time = new Date();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
            String t = dateFormat.format(time);
            t = t.substring(0, t.length() - 2) + ":" + t.substring(t.length() - 2, t.length());

            doc.put("type", "listener_telemetry");
            doc.put("time_created", t);
            doc.put("time_uploaded", t);

            JSONObject client = new JSONObject();
            JSONObject l_info = new JSONObject();
            client.put("device", device);
            client.put("device_software", device_software);
            client.put("application", application);
            client.put("application_version", application_version);

            JSONObject data = _listener_info.getJSONDataFieldTelem();
            data.put("client", client);

            doc.put("data", data);

            doc_info.put("data", l_info);

            String sha = _listener_info.toSha256();

            db.saveDocument(doc, sha);
            CouchResponse cr = s.getLastResponse();
            System.out.println(cr);
            if (cr.isOk())
                _listener_telem_UUID = sha;

        } catch (Exception e) {
            return false;
        }
        return true;
    }

    private boolean update_listener_info() {
        try {

            Document doc_info = new Document();

            //date uploaded
            Date time = new Date();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
            String t = dateFormat.format(time);
            t = t.substring(0, t.length() - 2) + ":" + t.substring(t.length() - 2, t.length());

            doc_info.put("type", "listener_information");
            doc_info.put("time_created", t);
            doc_info.put("time_uploaded", t);

            doc_info.put("data", _listener_info.getJSONDataFieldInfo());

            String sha_info = toSha256(doc_info.toString());

            db.saveDocument(doc_info, sha_info);
            CouchResponse cr = s.getLastResponse();
            System.out.println(cr);
            if (cr.isOk())
                _listener_info_UUID = sha_info;
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    public boolean newTelemConfigs() {
        return _newTelemConfigs;
    }

    public ConcurrentHashMap<String, TelemetryConfig> getTelemConfigs() {
        _newTelemConfigs = false;
        return telem_configs;
    }

    class QueueItem {
        public int type; //0: upload payload, 1: get payload telem, 2: update active flight list, 3: update chase car
        public String callsign = "";
        public long startTime = 0;
        public long stopTime = 0;
        public int count = 0;

        public QueueItem(int _type, int _count) {
            type = _type;
            count = _count;
        }

        public QueueItem(int _type, String _callsign, long _startTime, long _stopTime, int _count) {
            type = _type;
            callsign = _callsign;
            startTime = _startTime;
            stopTime = _stopTime;
            count = _count;
        }
    }

    public static String toSha256(String str) {

        byte[] enc = Base64.encodeBase64(str.getBytes());
        byte[] sha = null;

        MessageDigest md;
        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update(enc);
            sha = md.digest();
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return bytesToHexStr(sha);
    }

    //ref: http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java
    public static String bytesToHexStr(byte[] bytes) {
        final char[] hexArray = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        char[] hexChars = new char[bytes.length * 2];
        int v;
        for (int j = 0; j < bytes.length; j++) {
            v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }
}