net.epsilony.utils.codec.modbus.ModbusClientMaster.java Source code

Java tutorial

Introduction

Here is the source code for net.epsilony.utils.codec.modbus.ModbusClientMaster.java

Source

/*
 *
 * The MIT License (MIT)
 * 
 * Copyright (C) 2013 Man YUAN <epsilon@epsilony.net>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package net.epsilony.utils.codec.modbus;

import java.rmi.ConnectException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ChannelFactory;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.epsilony.utils.codec.modbus.reqres.ModbusRequest;
import net.epsilony.utils.codec.modbus.reqres.ModbusResponse;

/**
 * @author <a href="mailto:epsilony@epsilony.net">Man YUAN</a>
 *
 */
public class ModbusClientMaster {

    private Bootstrap bootstrap;
    private ChannelFuture connectFuture;
    private Channel channel;
    private ReentrantLock lock = new ReentrantLock();
    protected SimpModbusMasterChannelInitializer initializer;

    private TransectionIdDispatcher transectionIdDispatcher = new SimpTransectionIdDispatcher();
    private EventLoopGroup group;
    private int inetPort;
    private String innetAddress;

    private boolean keepAlive = true;
    private int socketTimeout = 5000;
    private int connectTimeout = 5000;
    private long requestLifeTime = 5000;
    private boolean tcpNoDelay = true;
    private ChannelFactory<Channel> channelFactory = new ChannelFactory<Channel>() {

        @Override
        public Channel newChannel() {
            return new NioSocketChannel();
        }
    };

    protected ChannelFuture genConnectFuture() {

        initializer = new SimpModbusMasterChannelInitializer();
        initializer.setRequestLifeTime(requestLifeTime);
        bootstrap = new Bootstrap();
        bootstrap.group(group).channelFactory(channelFactory).handler(initializer)
                .option(ChannelOption.TCP_NODELAY, tcpNoDelay).option(ChannelOption.SO_KEEPALIVE, keepAlive)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout)
                .option(ChannelOption.SO_TIMEOUT, socketTimeout);
        return bootstrap.connect(innetAddress, inetPort);
    }

    public CompletableFuture<ModbusResponse> request(ModbusRequest req) {
        final CompletableFuture<ModbusResponse> result = new CompletableFuture<>();

        result.whenComplete((res, ex) -> {
            if (transectionIdDispatcher != null) {
                int transectionId = req.getTransectionId();
                if (transectionId >= 0) {
                    transectionIdDispatcher.repay(transectionId);
                }
            }
        });

        lock.lock();
        try {
            if (connectFuture == null) {
                connectFuture = genConnectFuture();
                connectFuture.addListener(new GenericFutureListener<Future<? super Void>>() {

                    @Override
                    public void operationComplete(Future<? super Void> future) throws Exception {
                        lock.lock();
                        try {
                            if (future.isSuccess()) {
                                channel = connectFuture.channel();
                                channel.closeFuture()
                                        .addListener(new GenericFutureListener<Future<? super Void>>() {

                                            @Override
                                            public void operationComplete(Future<? super Void> future)
                                                    throws Exception {
                                                lock.lock();
                                                try {
                                                    connectFuture = null;
                                                    initializer.clearExceptionally(
                                                            new ConnectException("connection is closed!"));
                                                    transectionIdDispatcher.reset();
                                                } finally {
                                                    lock.unlock();
                                                }
                                            }
                                        });
                            } else {
                                connectFuture = null;
                            }
                        } finally {
                            lock.unlock();
                        }
                    }
                });
            }

            connectFuture.addListener(new GenericFutureListener<Future<? super Void>>() {

                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    ChannelFuture channelFuture = (ChannelFuture) future;
                    if (future.isSuccess()) {
                        if (null != transectionIdDispatcher) {
                            int transectionId = transectionIdDispatcher.borrow();
                            req.setTransectionId(transectionId);
                            if (transectionId < 0) {
                                result.completeExceptionally(new TransectionDispatcherEmptyException());
                                return;
                            }
                        }

                        try {
                            initializer.register(result, req);
                        } catch (Throwable ex) {
                            result.completeExceptionally(ex);
                            return;
                        }

                        ChannelFuture writeFuture = channelFuture.channel().writeAndFlush(req);
                        writeFuture.addListener(new GenericFutureListener<Future<? super Void>>() {

                            @Override
                            public void operationComplete(Future<? super Void> future) throws Exception {
                                if (!future.isSuccess()) {
                                    if (future.isCancelled()) {
                                        initializer.removeExceptionally(req.getTransectionId(),
                                                new ConnectException("connection is canncelled"));
                                    } else {
                                        initializer.removeExceptionally(req.getTransectionId(), future.cause());
                                    }
                                }
                            }
                        });
                    } else if (future.isCancelled()) {
                        result.completeExceptionally(new ConnectException("connection is canncelled"));
                    } else {
                        result.completeExceptionally(future.cause());
                    }
                }

            });
        } finally {
            lock.unlock();
        }

        return result;

    }

    public long getRequestLifeTime() {
        return requestLifeTime;
    }

    public void setRequestLifeTime(long requestLifeTime) {
        this.requestLifeTime = requestLifeTime;
    }

    public EventLoopGroup getGroup() {
        return group;
    }

    public void setGroup(EventLoopGroup group) {
        this.group = group;
    }

    public int getInetPort() {
        return inetPort;
    }

    public void setInetPort(int inetPort) {
        this.inetPort = inetPort;
    }

    public String getInnetAddress() {
        return innetAddress;
    }

    public void setInnetAddress(String innetAddress) {
        this.innetAddress = innetAddress;
    }

    public boolean isKeepAlive() {
        return keepAlive;
    }

    public void setKeepAlive(boolean keepAlive) {
        this.keepAlive = keepAlive;
    }

    public int getSocketTimeout() {
        return socketTimeout;
    }

    public void setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    public boolean isTcpNoDelay() {
        return tcpNoDelay;
    }

    public void setTcpNoDelay(boolean tcpNoDelay) {
        this.tcpNoDelay = tcpNoDelay;
    }

    public ChannelFactory<Channel> getChannelFactory() {
        return channelFactory;
    }

    public void setChannelFactory(ChannelFactory<Channel> channelFactory) {
        this.channelFactory = channelFactory;
    }

    public TransectionIdDispatcher getTransectionIdDispatcher() {
        return transectionIdDispatcher;
    }

    public void setTransectionIdDispatcher(TransectionIdDispatcher transectionIdDispatcher) {
        this.transectionIdDispatcher = transectionIdDispatcher;
    }

}