TaiSync.java 9.79 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 31

    private static final int TELEM_PORT = 14550;
    private static final int VIDEO_PORT = 5000;
32 33 34
    private static final int TAISYNC_VIDEO_PORT = 8000;
    private static final int TAISYNC_SETTINGS_PORT = 8200;
    private static final int TAISYNC_TELEMETRY_PORT = 8400;
35 36

    private boolean running = false;
37 38 39 40
    private DatagramSocket udpSocket = null;
    private Socket tcpSocket = null;
    private InputStream tcpInStream = null;
    private OutputStream tcpOutStream = null;
41 42 43 44 45 46 47 48 49
    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()
    {
50
        mThreadPool = Executors.newFixedThreadPool(3);
51 52 53 54
    }

    public boolean isRunning()
    {
55
        synchronized (this) {
56 57 58 59 60 61 62 63
            return running;
        }
    }

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

64
        synchronized (this) {
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
            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);

80
        udpSocket = new DatagramSocket();
81
        final InetAddress address = InetAddress.getByName("localhost");
82 83 84
        tcpSocket = new Socket(address, TAISYNC_SETTINGS_PORT);
        tcpInStream = tcpSocket.getInputStream();
        tcpOutStream = tcpSocket.getOutputStream();
85 86

        // Request connection packet
87
        sendTaiSyncMessage(PROTOCOL_REQUEST_CONNECTION, 0, null, 0);
88

89
        // Read multiplexed data stream coming from TaiSync accessory
90 91 92 93 94 95 96 97
        mThreadPool.execute(new Runnable() {
            @Override
            public void run()
            {
                int bytesRead = 0;

                try {
                    while (bytesRead >= 0) {
98
                        synchronized (this) {
99 100 101 102 103 104 105 106 107 108 109 110 111
                            if (!running) {
                                break;
                            }
                        }

                        bytesRead = mFileInputStream.read(mBytes);

                        if (bytesRead > 0)
                        {
                            if (mBytes[3] == PROTOCOL_VERSION)
                            {
                                vMaj = mBytes[19];
                                Log.i("QGC_TaiSync", "Got protocol version message vMaj = " + mBytes[19]);
112
                                sendTaiSyncMessage(PROTOCOL_VERSION, 0, null, 0);
113 114 115 116 117
                            }
                            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);
                                Log.i("QGC_TaiSync", "Read 2 port = " + dPort + " length = " + dLength);
118
                                sendTaiSyncMessage(PROTOCOL_CHANNEL, dPort, null, 0);
119 120 121 122 123
                            }
                            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);

124
                                int payloadOffset = HEADER_SIZE;
125 126
                                int payloadLength = bytesRead - payloadOffset;

127 128 129 130
                                byte[] sBytes = new byte[payloadLength];
                                System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength);

                                if (dPort == TAISYNC_VIDEO_PORT) {
131
                                    DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, VIDEO_PORT);
132 133 134 135
                                    udpSocket.send(packet);
                                } else if (dPort == TAISYNC_SETTINGS_PORT) {
                                    tcpOutStream.write(sBytes);
                                } else if (dPort == TAISYNC_TELEMETRY_PORT) {
136
                                    DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, TELEM_PORT);
137
                                    udpSocket.send(packet);
138 139 140 141 142 143 144 145 146 147 148 149 150
                                }
                            }
                        }
                    }
                } catch (IOException e) {
                    Log.e("QGC_TaiSync", "Exception: " + e);
                    e.printStackTrace();
                } finally {
                    close();
                }
            }
        });

151
        // Read command & control packets to be sent to telemetry port
152 153 154 155 156 157 158 159
        mThreadPool.execute(new Runnable() {
            @Override
            public void run()
            {
                byte[] inbuf = new byte[256];

                try {
                    while (true) {
160
                        synchronized (this) {
161 162 163 164 165 166
                            if (!running) {
                                break;
                            }
                        }

                        DatagramPacket packet = new DatagramPacket(inbuf, inbuf.length);
167
                        udpSocket.receive(packet);
168
                        sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, packet.getData(), packet.getLength());
169 170 171 172 173 174 175 176 177
                    }
                } catch (IOException e) {
                    Log.e("QGC_TaiSync", "Exception: " + e);
                    e.printStackTrace();
                } finally {
                    close();
                }
            }
        });
178

179 180 181 182 183 184
        // Read incoming requests for settings socket
        mThreadPool.execute(new Runnable() {
            @Override
            public void run()
            {
                byte[] inbuf = new byte[1024];
185

186 187 188 189 190 191
                try {
                    while (true) {
                        synchronized (this) {
                            if (!running) {
                                break;
                            }
192 193
                        }

194 195
                        int bytesRead = tcpInStream.read(inbuf, 0, inbuf.length);
                        if (bytesRead > 0) {
196
                            sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_SETTINGS_PORT, inbuf, bytesRead);
197
                        }
198 199 200 201 202 203 204 205 206 207 208
                    }
                } catch (IOException e) {
                    Log.e("QGC_TaiSync", "Exception: " + e);
                    e.printStackTrace();
                } finally {
                    close();
                }
            }
        });
    }

209
    private void sendTaiSyncMessage(byte protocol, int dataPort, byte[] data, int dataLen) throws IOException
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    {
        byte portMSB = (byte)((dataPort >> 8) & 0xFF);
        byte portLSB = (byte)(dataPort & 0xFF);

        byte[] lA = new byte[4];
        int len = HEADER_SIZE + dataLen;
        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);
        }

236 237 238
        synchronized (this) {
            mFileOutputStream.write(buffer);
        }
239 240
    }

241 242 243
    public void close()
    {
//        Log.i("QGC_TaiSync", "Close");
244
        synchronized (this) {
245 246 247
            running = false;
        }
        try {
248 249 250 251 252 253 254 255
            if (udpSocket != null) {
                udpSocket.close();
            }
        } catch (Exception e) {
        }
        try {
            if (tcpSocket != null) {
                tcpSocket.close();
256
            }
257 258 259
        } catch (Exception e) {
        }
        try {
260 261 262 263 264
            if (mParcelFileDescriptor != null) {
                mParcelFileDescriptor.close();
            }
        } catch (Exception e) {
        }
265 266 267 268 269
        udpSocket = null;
        tcpSocket = null;
        tcpInStream = null;
        tcpOutStream = null;
        mParcelFileDescriptor = null;
270 271
    }
}