package com.robot.contrib.netty.rxtx; import com.robot.contrib.netty.ConnectionEventListener; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.oio.OioEventLoopGroup; import io.netty.channel.rxtx.RxtxChannel; import io.netty.channel.rxtx.RxtxChannelOption; import io.netty.channel.rxtx.RxtxDeviceAddress; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.timeout.IdleStateHandler; import io.netty.util.concurrent.ScheduledFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; /** * RXTX渠道管理器 * * @author Laotang * @date 2020-02-19 * @since 1.0 */ public class RxtxClientChannelManager { private static final Logger LOG = LoggerFactory.getLogger(RxtxClientChannelManager.class); private final ConnectionEventListener connectionEventListener; private final Supplier<List<ChannelHandler>> channelSupplier; private ChannelFuture channelFuture; private boolean initialized; private int readTimeout; private ScheduledFuture<?> connectFuture; private boolean loggingEnabled; private Bootstrap bootstrap; private OioEventLoopGroup workerGroup; private static final String LOGGING_HANDLER_NAME = "RxtxChannelLoggingHandler"; public RxtxClientChannelManager(@Nonnull ConnectionEventListener connectionEventListener, Supplier<List<ChannelHandler>> channelSupplier, int readTimeout, boolean enableLogging) { this.connectionEventListener = requireNonNull(connectionEventListener, "connEventListener"); this.channelSupplier = requireNonNull(channelSupplier, "channelSupplier"); this.readTimeout = readTimeout; this.loggingEnabled = enableLogging; } public void initialize() { if (this.initialized) { LOG.warn("已经初始化,请勿重复初始化"); return; } RxtxClientHandler rxtxHandler = new RxtxClientHandler(connectionEventListener); this.bootstrap = new Bootstrap(); workerGroup = new OioEventLoopGroup(); this.bootstrap.group(workerGroup) .channel(RxtxChannel.class) .handler(new ChannelInitializer<RxtxChannel>() { @Override protected void initChannel(RxtxChannel ch) throws Exception { if (loggingEnabled) { ch.pipeline().addFirst(LOGGING_HANDLER_NAME, new LoggingHandler(RxtxClientChannelManager.this.getClass())); } if (readTimeout > 0) { ch.pipeline().addLast(new IdleStateHandler(readTimeout, 0, 0, TimeUnit.MILLISECONDS)); } for (ChannelHandler handler : channelSupplier.get()) { ch.pipeline().addLast(handler); } ch.pipeline().addLast(rxtxHandler); } }); initialized = true; } public void connect(@Nonnull String host, int port) { requireNonNull(host, "host"); checkState(isInitialized(), "Not initialized"); if (isConnected()) { LOG.debug("Already connected, doing nothing."); return; } try { bootstrap.option(RxtxChannelOption.BAUD_RATE, port); channelFuture = bootstrap.connect(new RxtxDeviceAddress(host)).sync(); channelFuture.addListener((ChannelFuture future) -> { if (future.isSuccess()) { this.initialized = true; LOG.info("串口连接并监听成功,名称[{}],波特率[{}]", host, port); connectionEventListener.onConnect(); } else { connectionEventListener.onFailedConnectionAttempt(); LOG.info("打开串口时失败,名称[" + host + "], 波特率[" + port + "], 串口可能已被占用!"); } }); connectFuture = null; } catch (Exception e) { e.printStackTrace(); workerGroup.shutdownGracefully(); throw new RuntimeException("RxtxClientChannelManager initialized is " + isInitialized() + ", exception message: " + e.getMessage()); } } public boolean isInitialized() { return initialized; } public boolean isConnected() { return channelFuture != null && channelFuture.channel().isActive(); } public void disconnect() { if (!isConnected()) { return; } if (channelFuture != null) { channelFuture.channel().disconnect(); channelFuture = null; } } public void scheduleConnect(@Nonnull String host, int port, long delay) { requireNonNull(host, "host"); checkState(isInitialized(), "Not initialized"); checkState(connectFuture == null, "Connection attempt already scheduled"); connectFuture = workerGroup.schedule(() -> connect(host, port), delay, TimeUnit.MILLISECONDS); } public void setLoggingEnabled(boolean enabled) { checkState(initialized, "Not initialized."); if (channelFuture == null) { LOG.debug("No channel future available, doing nothing."); return; } ChannelPipeline pipeline = channelFuture.channel().pipeline(); if (enabled && pipeline.get(LOGGING_HANDLER_NAME) == null) { pipeline.addFirst(LOGGING_HANDLER_NAME, new LoggingHandler(RxtxClientChannelManager.this.getClass())); } else if (!enabled && pipeline.get(LOGGING_HANDLER_NAME) != null) { pipeline.remove(LOGGING_HANDLER_NAME); } } public void cancelConnect() { if (connectFuture == null) { return; } connectFuture.cancel(false); connectFuture = null; } public void terminate() { if (!initialized) { return; } cancelConnect(); disconnect(); workerGroup.shutdownGracefully(); workerGroup = null; bootstrap = null; initialized = false; } /** * 发送报文 * * @param telegram * @throws Exception */ public void send(String telegram) throws Exception { channelFuture.channel().writeAndFlush(telegram); LOG.info("send telegram: {}", telegram); } }