001package edu.wpi.first.wpilibj.networktables2.client; 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 server 014 * 015 * @author Mitchell 016 * 017 */ 018public class ClientConnectionAdapter implements ConnectionAdapter, IncomingEntryReceiver, FlushableOutgoingEntryReceiver{ 019 020 private final ClientNetworkTableEntryStore entryStore; 021 private final IOStreamFactory streamFactory; 022 private final NTThreadManager threadManager; 023 024 private NetworkTableConnection connection; 025 private NTThread readThread; 026 private ClientConnectionState connectionState = ClientConnectionState.DISCONNECTED_FROM_SERVER; 027 private final ClientConnectionListenerManager connectionListenerManager; 028 private final Object connectionLock = new Object(); 029 private final NetworkTableEntryTypeManager typeManager; 030 031 private void gotoState(ClientConnectionState newState){ 032 synchronized(connectionLock){ 033 if(connectionState!=newState){ 034 System.out.println(this+" entered connection state: "+newState); 035 if(newState==ClientConnectionState.IN_SYNC_WITH_SERVER) 036 connectionListenerManager.fireConnectedEvent(); 037 if(connectionState==ClientConnectionState.IN_SYNC_WITH_SERVER) 038 connectionListenerManager.fireDisconnectedEvent(); 039 connectionState = newState; 040 } 041 } 042 } 043 /** 044 * @return the state of the connection 045 */ 046 public ClientConnectionState getConnectionState(){ 047 return connectionState; 048 } 049 /** 050 * @return if the client is connected to the server 051 */ 052 public boolean isConnected() { 053 return getConnectionState()==ClientConnectionState.IN_SYNC_WITH_SERVER; 054 } 055 056 /** 057 * Create a new ClientConnectionAdapter 058 * @param entryStore 059 * @param threadManager 060 * @param streamFactory 061 * @param transactionPool 062 * @param connectionListenerManager 063 */ 064 public ClientConnectionAdapter(final ClientNetworkTableEntryStore entryStore, final NTThreadManager threadManager, final IOStreamFactory streamFactory, final ClientConnectionListenerManager connectionListenerManager, final NetworkTableEntryTypeManager typeManager) { 065 this.entryStore = entryStore; 066 this.streamFactory = streamFactory; 067 this.threadManager = threadManager; 068 this.connectionListenerManager = connectionListenerManager; 069 this.typeManager = typeManager; 070 } 071 072 073 074 /* 075 * Connection management 076 */ 077 /** 078 * Reconnect the client to the server (even if the client is not currently connected) 079 */ 080 public void reconnect() { 081 synchronized(connectionLock){ 082 close();//close the existing stream and monitor thread if needed 083 try{ 084 IOStream stream = streamFactory.createStream(); 085 if(stream==null) 086 return; 087 connection = new NetworkTableConnection(stream, typeManager); 088 readThread = threadManager.newBlockingPeriodicThread(new ConnectionMonitorThread(this, connection), "Client Connection Reader Thread"); 089 connection.sendClientHello(); 090 gotoState(ClientConnectionState.CONNECTED_TO_SERVER); 091 } catch(Exception e){ 092 close();//make sure to clean everything up if we fail to connect 093 } 094 } 095 } 096 097 /** 098 * Close the client connection 099 */ 100 public void close() { 101 close(ClientConnectionState.DISCONNECTED_FROM_SERVER); 102 } 103 /** 104 * Close the connection to the server and enter the given state 105 * @param newState 106 */ 107 public void close(final ClientConnectionState newState) { 108 synchronized(connectionLock){ 109 gotoState(newState); 110 if(readThread!=null){ 111 readThread.stop(); 112 readThread = null; 113 } 114 if(connection!=null){ 115 connection.close(); 116 connection = null; 117 } 118 entryStore.clearIds(); 119 } 120 } 121 122 123 124 public void badMessage(BadMessageException e) { 125 close(new ClientConnectionState.Error(e)); 126 } 127 128 public void ioException(IOException e) { 129 if(connectionState!=ClientConnectionState.DISCONNECTED_FROM_SERVER)//will get io exception when on read thread connection is closed 130 reconnect(); 131 //gotoState(new ClientConnectionState.Error(e)); 132 } 133 134 public NetworkTableEntry getEntry(char id) { 135 return entryStore.getEntry(id); 136 } 137 138 139 public void keepAlive() throws IOException { 140 } 141 142 public void clientHello(char protocolRevision) throws IOException { 143 throw new BadMessageException("A client should not receive a client hello message"); 144 } 145 146 public void protocolVersionUnsupported(char protocolRevision) { 147 close(); 148 gotoState(new ClientConnectionState.ProtocolUnsuppotedByServer(protocolRevision)); 149 } 150 151 public void serverHelloComplete() throws IOException { 152 if (connectionState==ClientConnectionState.CONNECTED_TO_SERVER) { 153 try { 154 gotoState(ClientConnectionState.IN_SYNC_WITH_SERVER); 155 entryStore.sendUnknownEntries(connection); 156 } catch (IOException e) { 157 ioException(e); 158 } 159 } 160 else 161 throw new BadMessageException("A client should only receive a server hello complete once and only after it has connected to the server"); 162 } 163 164 165 public void offerIncomingAssignment(NetworkTableEntry entry) { 166 entryStore.offerIncomingAssignment(entry); 167 } 168 public void offerIncomingUpdate(NetworkTableEntry entry, char sequenceNumber, Object value) { 169 entryStore.offerIncomingUpdate(entry, sequenceNumber, value); 170 } 171 172 public void offerOutgoingAssignment(NetworkTableEntry entry) { 173 try { 174 synchronized(connectionLock){ 175 if(connection!=null && connectionState==ClientConnectionState.IN_SYNC_WITH_SERVER) 176 connection.sendEntryAssignment(entry); 177 } 178 } catch(IOException e){ 179 ioException(e); 180 } 181 } 182 183 public void offerOutgoingUpdate(NetworkTableEntry entry) { 184 try { 185 synchronized(connectionLock){ 186 if(connection!=null && connectionState==ClientConnectionState.IN_SYNC_WITH_SERVER) 187 connection.sendEntryUpdate(entry); 188 } 189 } catch(IOException e){ 190 ioException(e); 191 } 192 } 193 public void flush() { 194 synchronized(connectionLock){ 195 if(connection!=null) { 196 try { 197 connection.flush(); 198 } catch (IOException e) { 199 ioException(e); 200 } 201 } 202 } 203 } 204 public void ensureAlive() { 205 synchronized(connectionLock){ 206 if(connection!=null) { 207 try { 208 connection.sendKeepAlive(); 209 } catch (IOException e) { 210 ioException(e); 211 } 212 } 213 else 214 reconnect();//try to reconnect if not connected 215 } 216 } 217 218}