TaiSync.java 10.4 KB
Newer Older
1 2 3 4 5
package org.mavlink.qgroundcontrol;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

6 7
import java.io.InputStream;
import java.io.OutputStream;
8 9 10 11 12 13 14
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
15
import java.net.Socket;
16 17 18 19 20 21 22
import java.net.InetAddress;

import android.os.ParcelFileDescriptor;
import android.util.Log;

public class TaiSync
{
23 24 25 26 27 28
    private static final int HEADER_SIZE = 0x1C;

    private static final byte PROTOCOL_REQUEST_CONNECTION = 0x00;
    private static final byte PROTOCOL_VERSION = 0x01;
    private static final byte PROTOCOL_CHANNEL = 0x02;
    private static final byte PROTOCOL_DATA = 0x03;
29

30
    private static final int VIDEO_PORT = 5600;
31 32 33
    private static final int TAISYNC_VIDEO_PORT = 8000;
    private static final int TAISYNC_SETTINGS_PORT = 8200;
    private static final int TAISYNC_TELEMETRY_PORT = 8400;
34

35
    private Object runLock;
36
    private boolean running = false;
37
    private DatagramSocket udpSocket = null;
38 39 40 41 42 43
    private Socket tcpSettingsSocket = null;
    private InputStream settingsInStream = null;
    private OutputStream settingsOutStream = null;
    private Socket tcpTelemetrySocket = null;
    private InputStream telemetryInStream = null;
    private OutputStream telemetryOutStream = null;
44 45 46 47 48 49 50 51 52
    private ParcelFileDescriptor mParcelFileDescriptor;
    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;
    private ExecutorService mThreadPool;
    private byte[] mBytes = new byte[32 * 1024];
    private byte vMaj = 0;

    public TaiSync()
    {
53
        runLock = new Object();
54
        mThreadPool = Executors.newFixedThreadPool(3);
55 56 57 58
    }

    public boolean isRunning()
    {
59
        synchronized (runLock) {
60 61 62 63 64 65 66 67
            return running;
        }
    }

    public void open(ParcelFileDescriptor parcelFileDescriptor) throws IOException
    {
//        Log.i("QGC_TaiSync", "Open");

68
        synchronized (runLock) {
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
            if (running) {
                return;
            }
            running = true;
        }

        mParcelFileDescriptor = parcelFileDescriptor;
        if (mParcelFileDescriptor == null) {
            throw new IOException("parcelFileDescriptor is null");
        }

        FileDescriptor fileDescriptor = mParcelFileDescriptor.getFileDescriptor();
        mFileInputStream = new FileInputStream(fileDescriptor);
        mFileOutputStream = new FileOutputStream(fileDescriptor);

84
        udpSocket = new DatagramSocket();
85
        final InetAddress address = InetAddress.getByName("localhost");
86 87 88 89 90 91
        tcpTelemetrySocket = new Socket(address, TAISYNC_TELEMETRY_PORT);
        telemetryInStream = tcpTelemetrySocket.getInputStream();
        telemetryOutStream = tcpTelemetrySocket.getOutputStream();
        tcpSettingsSocket = new Socket(address, TAISYNC_SETTINGS_PORT);
        settingsInStream = tcpSettingsSocket.getInputStream();
        settingsOutStream = tcpSettingsSocket.getOutputStream();
92 93

        // Request connection packet
94
        sendTaiSyncMessage(PROTOCOL_REQUEST_CONNECTION, 0, null, 0);
95

96
        // Read multiplexed data stream coming from TaiSync accessory
97 98 99 100 101 102 103 104
        mThreadPool.execute(new Runnable() {
            @Override
            public void run()
            {
                int bytesRead = 0;

                try {
                    while (bytesRead >= 0) {
105
                        synchronized (runLock) {
106 107 108 109 110 111 112 113 114 115 116 117
                            if (!running) {
                                break;
                            }
                        }

                        bytesRead = mFileInputStream.read(mBytes);

                        if (bytesRead > 0)
                        {
                            if (mBytes[3] == PROTOCOL_VERSION)
                            {
                                vMaj = mBytes[19];
118
                                // Log.i("QGC_TaiSync", "Got protocol version message vMaj = " + mBytes[19]);
119
                                sendTaiSyncMessage(PROTOCOL_VERSION, 0, null, 0);
120 121 122 123
                            }
                            else if (mBytes[3] == PROTOCOL_CHANNEL) {
                                int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff);
                                int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff);
124
                                // Log.i("QGC_TaiSync", "Read 2 port = " + dPort + " length = " + dLength);
125
                                sendTaiSyncMessage(PROTOCOL_CHANNEL, dPort, null, 0);
126 127 128 129 130
                            }
                            else if (mBytes[3] == PROTOCOL_DATA) {
                                int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff);
                                int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff);

131
                                int payloadOffset = HEADER_SIZE;
132 133
                                int payloadLength = bytesRead - payloadOffset;

134 135 136 137
                                byte[] sBytes = new byte[payloadLength];
                                System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength);

                                if (dPort == TAISYNC_VIDEO_PORT) {
138
                                    DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, VIDEO_PORT);
139 140
                                    udpSocket.send(packet);
                                } else if (dPort == TAISYNC_SETTINGS_PORT) {
141
                                    settingsOutStream.write(sBytes);
142
                                } else if (dPort == TAISYNC_TELEMETRY_PORT) {
143
                                    telemetryOutStream.write(sBytes);
144 145 146 147 148 149 150 151 152 153 154 155 156
                                }
                            }
                        }
                    }
                } catch (IOException e) {
                    Log.e("QGC_TaiSync", "Exception: " + e);
                    e.printStackTrace();
                } finally {
                    close();
                }
            }
        });

157
        // Read command & control packets to be sent to telemetry port
158 159 160 161 162 163 164 165
        mThreadPool.execute(new Runnable() {
            @Override
            public void run()
            {
                byte[] inbuf = new byte[256];

                try {
                    while (true) {
166
                        synchronized (runLock) {
167 168 169 170 171
                            if (!running) {
                                break;
                            }
                        }

172 173 174 175
                        int bytesRead = telemetryInStream.read(inbuf, 0, inbuf.length);
                        if (bytesRead > 0) {
                            sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, inbuf, bytesRead);
                        }
176 177 178 179 180 181 182 183 184
                    }
                } catch (IOException e) {
                    Log.e("QGC_TaiSync", "Exception: " + e);
                    e.printStackTrace();
                } finally {
                    close();
                }
            }
        });
185

186 187 188 189 190 191
        // Read incoming requests for settings socket
        mThreadPool.execute(new Runnable() {
            @Override
            public void run()
            {
                byte[] inbuf = new byte[1024];
192

193 194
                try {
                    while (true) {
195
                        synchronized (runLock) {
196 197 198
                            if (!running) {
                                break;
                            }
199 200
                        }

201
                        int bytesRead = settingsInStream.read(inbuf, 0, inbuf.length);
202
                        if (bytesRead > 0) {
203
                            sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_SETTINGS_PORT, inbuf, bytesRead);
204
                        }
205 206 207 208 209 210 211 212 213
                    }
                } catch (IOException e) {
                    Log.e("QGC_TaiSync", "Exception: " + e);
                    e.printStackTrace();
                } finally {
                    close();
                }
            }
        });
214
   }
215

216
    private void sendTaiSyncMessage(byte protocol, int dataPort, byte[] data, int dataLen) throws IOException
217 218 219 220 221 222
    {
        byte portMSB = (byte)((dataPort >> 8) & 0xFF);
        byte portLSB = (byte)(dataPort & 0xFF);

        byte[] lA = new byte[4];
        int len = HEADER_SIZE + dataLen;
223
        // Log.i("QGC_TaiSync", "Sending to " + dataPort + " length = " + len);
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
        byte[] buffer = new byte[len];

        for (int i = 3; i >= 0; i--) {
            lA[i] = (byte)(len & 0xFF);
            len >>= 8;
        }

        byte[] header = { 0x00, 0x00, 0x00, protocol,   // uint32 - protocol
                          0x00, 0x00, portMSB, portLSB, // uint32 - dport
                          lA[0], lA[1], lA[2], lA[3],   // uint32 - length
                          0x00, 0x00, 0x00, 0x00,       // uint32 - magic
                          0x00, 0x00, 0x00, vMaj,       // uint32 - version major
                          0x00, 0x00, 0x00, 0x00,       // uint32 - version minor
                          0x00, 0x00, 0x00, 0x00 };     // uint32 - padding

        System.arraycopy(header, 0, buffer, 0, header.length);
        if (data != null && dataLen > 0) {
            System.arraycopy(data, 0, buffer, header.length, dataLen);
        }

244
        synchronized (runLock) {
245 246
            mFileOutputStream.write(buffer);
        }
247 248
    }

249 250 251
    public void close()
    {
//        Log.i("QGC_TaiSync", "Close");
252
        synchronized (runLock) {
253 254 255
            running = false;
        }
        try {
256 257 258 259 260 261
            if (udpSocket != null) {
                udpSocket.close();
            }
        } catch (Exception e) {
        }
        try {
262 263 264 265 266 267 268 269
            if (tcpTelemetrySocket != null) {
                tcpTelemetrySocket.close();
            }
        } catch (Exception e) {
        }
        try {
            if (tcpSettingsSocket != null) {
                tcpSettingsSocket.close();
270
            }
271 272 273
        } catch (Exception e) {
        }
        try {
274 275 276 277 278
            if (mParcelFileDescriptor != null) {
                mParcelFileDescriptor.close();
            }
        } catch (Exception e) {
        }
279
        udpSocket = null;
280 281 282 283
        tcpSettingsSocket = null;
        tcpTelemetrySocket = null;
        settingsInStream = null;
        settingsOutStream = null;
284
        mParcelFileDescriptor = null;
285 286
    }
}