001package edu.wpi.first.wpilibj.networktables2.server;
002
003import edu.wpi.first.wpilibj.networktables2.connection.BadMessageException;
004import java.io.*;
005
006import edu.wpi.first.wpilibj.networktables2.*;
007import edu.wpi.first.wpilibj.networktables2.connection.*;
008import edu.wpi.first.wpilibj.networktables2.stream.*;
009import edu.wpi.first.wpilibj.networktables2.thread.*;
010import edu.wpi.first.wpilibj.networktables2.type.*;
011
012/**
013 * Object that adapts messages from a client to the server
014 * 
015 * @author Mitchell
016 *
017 */
018public class ServerConnectionAdapter implements ConnectionAdapter, IncomingEntryReceiver, FlushableOutgoingEntryReceiver{
019        
020        private final ServerNetworkTableEntryStore entryStore;
021        private final IncomingEntryReceiver transactionReceiver;
022        private final ServerAdapterManager adapterListener;
023        /**
024         * the connection this adapter uses
025         */
026        public final NetworkTableConnection connection;
027        private final NTThread readThread;
028        
029        private ServerConnectionState connectionState;
030        private void gotoState(ServerConnectionState newState){
031                if(connectionState!=newState){
032                        System.out.println(this+" entered connection state: "+newState);
033                        connectionState = newState;
034                }
035        }
036
037        /**
038         * Create a server connection adapter for a given stream
039         * 
040         * @param stream
041         * @param transactionPool
042         * @param entryStore
043         * @param transactionReceiver
044         * @param adapterListener
045         * @param threadManager
046         */
047        public ServerConnectionAdapter(final IOStream stream, final ServerNetworkTableEntryStore entryStore, final IncomingEntryReceiver transactionReceiver, final ServerAdapterManager adapterListener, final NetworkTableEntryTypeManager typeManager, final NTThreadManager threadManager) {
048                connection = new NetworkTableConnection(stream, typeManager);
049                this.entryStore = entryStore;
050                this.transactionReceiver = transactionReceiver;
051                this.adapterListener = adapterListener;
052                
053                gotoState(ServerConnectionState.GOT_CONNECTION_FROM_CLIENT);
054                readThread = threadManager.newBlockingPeriodicThread(new ConnectionMonitorThread(this, connection), "Server Connection Reader Thread");
055        }
056        
057
058        public void badMessage(BadMessageException e) {
059            gotoState(new ServerConnectionState.Error(e));
060            adapterListener.close(this, true);
061        }
062        public void ioException(IOException e) {
063            if(e instanceof EOFException)
064                gotoState(ServerConnectionState.CLIENT_DISCONNECTED);
065            else
066                gotoState(new ServerConnectionState.Error(e));
067            adapterListener.close(this, false);
068        }
069        
070        
071        /**
072         * stop the read thread and close the stream
073         */
074        public void shutdown(boolean closeStream) {
075                readThread.stop();
076                if(closeStream)
077                connection.close();
078        }
079
080        public void keepAlive() throws IOException {
081                //just let it happen
082        }
083
084        public void clientHello(char protocolRevision) throws IOException {
085                if(connectionState!=ServerConnectionState.GOT_CONNECTION_FROM_CLIENT)
086                        throw new BadMessageException("A server should not receive a client hello after it has already connected/entered an error state");
087                if(protocolRevision!=NetworkTableConnection.PROTOCOL_REVISION){
088                    connection.sendProtocolVersionUnsupported();
089                    throw new BadMessageException("Client Connected with bad protocol revision: 0x"+Integer.toHexString(protocolRevision));
090                }
091                else{
092                    entryStore.sendServerHello(connection);
093                    gotoState(ServerConnectionState.CONNECTED_TO_CLIENT);
094                }
095        }
096
097        public void protocolVersionUnsupported(char protocolRevision) throws IOException {
098                throw new BadMessageException("A server should not receive a protocol version unsupported message");
099        }
100
101        public void serverHelloComplete() throws IOException {
102                throw new BadMessageException("A server should not receive a server hello complete message");
103        }
104
105        public void offerIncomingAssignment(NetworkTableEntry entry) {
106                transactionReceiver.offerIncomingAssignment(entry);
107        }
108
109        public void offerIncomingUpdate(NetworkTableEntry entry, char sequenceNumber, Object value) {
110                transactionReceiver.offerIncomingUpdate(entry, sequenceNumber, value);
111        }
112
113        public NetworkTableEntry getEntry(char id) {
114                return entryStore.getEntry(id);
115        }
116
117        public void offerOutgoingAssignment(NetworkTableEntry entry) {
118                try {
119                        if(connectionState==ServerConnectionState.CONNECTED_TO_CLIENT)
120                                connection.sendEntryAssignment(entry);
121                } catch (IOException e) {
122                        ioException(e);
123                }
124        }
125        public void offerOutgoingUpdate(NetworkTableEntry entry) {
126                try {
127                        if(connectionState==ServerConnectionState.CONNECTED_TO_CLIENT)
128                                connection.sendEntryUpdate(entry);
129                } catch (IOException e) {
130                        ioException(e);
131                }
132        }
133
134
135        public void flush() {
136                try {
137                        connection.flush();
138                } catch (IOException e) {
139                        ioException(e);
140                }
141        }
142
143        /**
144         * @return the state of the connection
145         */
146        public ServerConnectionState getConnectionState() {
147            return connectionState;
148        }
149
150        public void ensureAlive() {
151            try {
152                    connection.sendKeepAlive();
153            } catch (IOException e) {
154                    ioException(e);
155            }
156        }
157
158}