com.digitalpetri.enip.ChannelManager Maven / Gradle / Ivy
package com.digitalpetri.enip;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import com.digitalpetri.enip.commands.RegisterSession;
import com.digitalpetri.enip.commands.UnRegisterSession;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ChannelManager {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final AtomicReference state = new AtomicReference<>(new Idle());
private final EtherNetIpClient client;
public ChannelManager(EtherNetIpClient client) {
this.client = client;
}
public CompletableFuture getChannel() {
State currentState = state.get();
if (currentState instanceof Idle) {
Connecting nextState = new Connecting();
if (state.compareAndSet(currentState, nextState)) {
CompletableFuture future = nextState.future;
future.whenComplete((ch, ex) -> {
if (ch != null) state.set(new Connected(future));
else state.set(new Idle());
});
return connect(future);
} else {
return getChannel();
}
} else if (currentState instanceof Connecting) {
return ((Connecting) currentState).future;
} else if (currentState instanceof Connected) {
return ((Connected) currentState).future;
} else {
throw new IllegalStateException(currentState.getClass().getSimpleName());
}
}
private CompletableFuture connect(CompletableFuture future) {
CompletableFuture bootstrap = EtherNetIpClient.bootstrap(client);
bootstrap.whenComplete((ch, ex) -> {
if (ch != null) {
logger.debug("Channel bootstrap succeeded: localAddress={}, remoteAddress={}",
ch.localAddress(), ch.remoteAddress());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
state.set(new Idle());
super.channelInactive(ctx);
}
});
CompletableFuture registerFuture = new CompletableFuture<>();
registerFuture.whenComplete((r, ex2) -> {
if (r != null) future.complete(ch);
else future.completeExceptionally(ex2);
});
client.writeCommand(ch, new RegisterSession(), registerFuture);
} else {
logger.debug("Channel bootstrap failed: {}", ex.getMessage(), ex);
future.completeExceptionally(ex);
}
});
return future;
}
public void disconnect() {
State currentState = state.get();
if (currentState instanceof Connecting) {
((Connecting) currentState).future.whenComplete((ch, ex) -> {
if (ch != null) ch.close();
});
} else if (currentState instanceof Connected) {
((Connected) currentState).future.whenComplete((ch, ex) -> {
CompletableFuture f = new CompletableFuture<>();
client.writeCommand(ch, new UnRegisterSession(), f);
f.whenComplete((cmd, ex2) -> ch.close());
});
}
}
public String getState() {
return state.get().getClass().getSimpleName();
}
private static abstract class State {
}
private static class Idle extends State {
}
private static class Connecting extends State {
private final CompletableFuture future = new CompletableFuture<>();
}
private static class Connected extends State {
private final CompletableFuture future;
private Connected(CompletableFuture future) {
this.future = future;
}
}
}