001package edu.wpi.first.wpilibj.networktables2.connection;
002
003import java.io.*;
004
005import edu.wpi.first.wpilibj.networktables2.*;
006import edu.wpi.first.wpilibj.networktables2.stream.*;
007import edu.wpi.first.wpilibj.networktables2.type.*;
008
009
010/**
011 * An abstraction for the NetworkTable protocol
012 * 
013 * @author mwills
014 *
015 */
016public class NetworkTableConnection {
017        public static final char PROTOCOL_REVISION = 0x0200;
018        
019        private final Object WRITE_LOCK = new Object();
020
021        private final DataInputStream is;
022        private final DataOutputStream os;
023        /**
024         * the raw stream that is used in this connection
025         */
026        public final IOStream stream;
027        private final NetworkTableEntryTypeManager typeManager;
028        private boolean isValid;
029
030        
031        public NetworkTableConnection(IOStream stream, NetworkTableEntryTypeManager typeManager){
032                this.stream = stream;
033                this.typeManager = typeManager;
034                this.is = new DataInputStream(new BufferedInputStream(stream.getInputStream()));
035                this.os = new DataOutputStream(new BufferedOutputStream(stream.getOutputStream()));
036                isValid = true;
037        }
038
039        public void close() {
040                if(isValid){
041                        isValid = false;
042                        stream.close();
043                }
044        }
045
046
047        private void sendMessageHeader(int messageType) throws IOException{
048               synchronized(WRITE_LOCK){
049                os.writeByte(messageType);
050               }
051        }
052        public void flush() throws IOException{
053            synchronized(WRITE_LOCK){
054                os.flush();
055            }
056        }
057
058
059
060        public void sendKeepAlive() throws IOException {
061            synchronized(WRITE_LOCK){
062                sendMessageHeader(NetworkTableMessageType.KEEP_ALIVE);
063                flush();
064            }
065        }
066
067        public void sendClientHello() throws IOException {
068            synchronized(WRITE_LOCK){
069                sendMessageHeader(NetworkTableMessageType.CLIENT_HELLO);
070                os.writeChar(PROTOCOL_REVISION);
071                flush();
072            }
073        }
074        
075        public void sendServerHelloComplete() throws IOException {
076            synchronized(WRITE_LOCK){
077                sendMessageHeader(NetworkTableMessageType.SERVER_HELLO_COMPLETE);
078                flush();
079            }
080        }
081
082        public void sendProtocolVersionUnsupported() throws IOException {
083            synchronized(WRITE_LOCK){
084                sendMessageHeader(NetworkTableMessageType.PROTOCOL_VERSION_UNSUPPORTED);
085                os.writeChar(PROTOCOL_REVISION);
086                flush();
087            }
088        }
089
090        
091        
092        public void sendEntryAssignment(NetworkTableEntry entry) throws IOException {
093            synchronized(WRITE_LOCK){
094                sendMessageHeader(NetworkTableMessageType.ENTRY_ASSIGNMENT);
095                os.writeUTF(entry.name);
096                os.writeByte(entry.getType().id);
097                os.writeChar(entry.getId());
098                os.writeChar(entry.getSequenceNumber());
099                entry.sendValue(os);
100            }
101        }
102        public void sendEntryUpdate(NetworkTableEntry entry) throws IOException {
103            synchronized(WRITE_LOCK){
104                sendMessageHeader(NetworkTableMessageType.FIELD_UPDATE);
105                os.writeChar(entry.getId());
106                os.writeChar(entry.getSequenceNumber());
107                entry.sendValue(os);
108            }
109        }
110
111        
112        
113        public void read(ConnectionAdapter adapter) throws IOException {
114                try{
115                        int messageType = is.readByte();
116                        switch(messageType){
117                        case NetworkTableMessageType.KEEP_ALIVE:
118                                adapter.keepAlive();
119                                return;
120                        case NetworkTableMessageType.CLIENT_HELLO:
121                        {
122                                char protocolRevision = is.readChar();
123                                adapter.clientHello(protocolRevision);
124                                return;
125                        }
126                        case NetworkTableMessageType.SERVER_HELLO_COMPLETE:
127                        {
128                                adapter.serverHelloComplete();
129                                return;
130                        }
131                        case NetworkTableMessageType.PROTOCOL_VERSION_UNSUPPORTED:
132                        {
133                                char protocolRevision = is.readChar();
134                                adapter.protocolVersionUnsupported(protocolRevision);
135                                return;
136                        }
137                        case NetworkTableMessageType.ENTRY_ASSIGNMENT:
138                        {
139                                String entryName = is.readUTF();
140                                byte typeId = is.readByte();
141                                NetworkTableEntryType entryType = typeManager.getType(typeId);
142                                if(entryType==null)
143                                        throw new BadMessageException("Unknown data type: 0x"+Integer.toHexString((int)typeId));
144                                char entryId = is.readChar();
145                                char entrySequenceNumber = is.readChar();
146                                Object value = entryType.readValue(is);
147                                adapter.offerIncomingAssignment(new NetworkTableEntry(entryId, entryName, entrySequenceNumber, entryType, value));
148                                return;
149                        }
150                        case NetworkTableMessageType.FIELD_UPDATE:
151                        {
152                                char entryId = is.readChar();
153                                char entrySequenceNumber = is.readChar();
154                                NetworkTableEntry entry = adapter.getEntry(entryId);
155                                if(entry==null)
156                                        throw new BadMessageException("Received update for unknown entry id: "+(int)entryId);
157                                Object value = entry.getType().readValue(is);
158                                
159                                adapter.offerIncomingUpdate(entry, entrySequenceNumber, value);
160                                return;
161                        }
162                        default:
163                                throw new BadMessageException("Unknown Network Table Message Type: "+messageType);
164                        }
165                } catch(IOException e){
166                        throw e;
167                }
168        }
169
170}