package com.jiayue.ipfcst.dataexchange.tunnelworker.gathers.iml;

import com.jiayue.ipfcst.common.data.constant.enums.TunnelStatus;
import com.jiayue.ipfcst.common.data.entity.gathertunnelinfo.GatherModbusTcpTunnelInfo;
import com.jiayue.ipfcst.dataexchange.exception.DataExchangeException;
import com.jiayue.ipfcst.dataexchange.tunnelworker.BaseProtocolTunnel;
import com.jiayue.ipfcst.dataexchange.tunnelworker.workassist.SingleThreadPoolExecutorUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.socket.SocketChannel;
import wei.yigulu.modbus.domain.FunctionCode;
import wei.yigulu.modbus.netty.HSModbusTcpMasterBuilder;
import wei.yigulu.modbus.netty.ModbusTcpDelimiterHandler;
import wei.yigulu.modbus.netty.ModbusTcpMasterBuilder;
import wei.yigulu.modbus.netty.ModbusTcpMasterHandler;
import wei.yigulu.netty.AbstractTcpMasterBuilder;
import wei.yigulu.netty.ProtocolChannelInitializer;

/**
 * modbus协议TCP数据采集
 *
 * @author 修唯xiuwei
 * @version 3.0
 */

public class ModbusTcpDataGather extends AbstractModbusDataGather<GatherModbusTcpTunnelInfo, ModbusTcpDataGather.ModbusMaster> {


  /**
   * 构造方法
   *
   * @param gatherModbusTcpTunnelInfo 通道信息
   */
  public ModbusTcpDataGather(GatherModbusTcpTunnelInfo gatherModbusTcpTunnelInfo) {
    super(gatherModbusTcpTunnelInfo);
  }

  @Override
  public ModbusTcpDataGather buildTunnel() throws DataExchangeException {
    protocolBuilder = new ModbusMaster(tunnelInfo.getRemoteIp(), tunnelInfo.getRemotePort());
    protocolBuilder.setSpareIp(tunnelInfo.getRemoteSpareIp());
    protocolBuilder.setSparePort(tunnelInfo.getRemoteSparePort());
    protocolBuilder.setSelfIp(tunnelInfo.getSelfIp());
    protocolBuilder.setSelfPort(tunnelInfo.getSelfPort());
    protocolBuilder.setLog(this.log);
    setSlaveId(this.tunnelInfo.getSlaveId());
    setFunctionCode(FunctionCode.valueOf(this.tunnelInfo.getFunctionCode()));
    this.parseGatherDataPoint();
    this.setTunnelStatus(TunnelStatus.BUILT);
    protocolTunnelContainer.addTunnel(this);
    log.info("成功创建ModbusTcpMaster通道对象：{}", this.tunnelInfo.getTunnelName());
    return this;
  }


  @Override
  public ModbusTcpDataGather startTunnel() throws DataExchangeException {
    log.info("modbus TCP master {} 通道开始连接", getTunnelInfo().getTunnelName());
    protocolTunnelContainer.addUpdateDateTask(this);
    try {
      SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> this.protocolBuilder.create());
    } catch (Exception e) {
      new DataExchangeException(10009, "modbus TCP master 通道创建时发生异常");
    }
    return this;
  }

  @Override
  public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
    if (this.protocolBuilder != null) {
      this.protocolBuilder.stop();
    }
    log.info("关闭 modbus TCP master 通道 {}", this.tunnelInfo.getTunnelName());
    return super.tunnelStop();
  }


  @Override
  public void getDataFromProtocol() throws DataExchangeException {
    try {
      super.getDataFromProtocol();
      this.consecutiveExceptions=0;
      this.setTunnelStatus(TunnelStatus.CONNECTED);
    }catch (Exception e){
      this.consecutiveExceptions++;
      if(consecutiveExceptions>10) {
        this.setTunnelStatus(TunnelStatus.CONNECTEDANDCOMMERROR);
      }
    }
  }

  class ModbusMaster extends HSModbusTcpMasterBuilder {

    public ModbusMaster(String ip, Integer port) {
      super(ip, port);
    }

    @Override
    protected ProtocolChannelInitializer getOrCreateChannelInitializer() {
      if (this.channelInitializer == null) {
        this.channelInitializer =  new ProtocolChannelInitializer<SocketChannel>(getProtocolBuilder()) {
          protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new ChannelHandler[]{(new ModbusTcpDelimiterHandler()).setLog(getLog()).setMaxTimeSpace(1000)});
            ch.pipeline().addLast(new ChannelHandler[]{new ModbusTcpMasterHandler((AbstractTcpMasterBuilder)this.builder)});
          }
        };
      }
      return this.channelInitializer;
    }


    @Override
    public void connected() {
      setTunnelStatus(TunnelStatus.CONNECTED);
    }

    @Override
    public void disconnected() {
      setTunnelStatus(TunnelStatus.LOSECONN);
    }
  }

}
