All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.digitalpetri.enip.ChannelManager Maven / Gradle / Ivy

There is a newer version: 1.5.0-RC1
Show newest version
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;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy