Netty实现连接西门子PLC

西门子PLC支持以太网通信,常用的通信协议是S7协议,S7协议本质上也属于TCP/IP的一种,TCP/IP通信一般是基于Socket实现的,考虑到Socket通信是I/O阻塞模式,在连接大批量PLC节点是效率不佳的,Netty是基于NIO基础上的封装,是一个高性能NIO框架,项目中立库需要连接上百个点位的PLC,由此引入Netty实现。

支持的西门子PLC版本:S200、S300 、S400 、S200Smart 、S1200 、S1500

<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
  <version>4.1.10.Final</version>
</dependency>

 Netty连接PLC核心类SiemensNet 

public class SiemensNet {
   private static final Logger log = LoggerFactory.getLogger(SiemensNet.class);
   private String plcIP = "127.0.0.1";
   private Bootstrap bootstrap = new Bootstrap();
   private EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
   private SiemensHandler siemensHandler = new SiemensHandler();
   private String errorMsg = "msg";

   public SiemensNet(SiemensPLCS type) {
      this.siemensHandler.setHead(type);
      this.bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
      ((((this.bootstrap.group(this.eventLoopGroup))
              .channel(NioSocketChannel.class))
              .option(ChannelOption.TCP_NODELAY, true))
              .option(ChannelOption.SO_KEEPALIVE, true))
              .handler(new ChannelInitializer<SocketChannel>() {
         protected void initChannel(SocketChannel channel) throws Exception {
            channel.pipeline().addLast("SiemensDecoder", new SiemensDecoder());
            channel.pipeline().addLast("SiemensEncoder", new SiemensEncoder());
            channel.pipeline().addLast("SiemensHandler", SiemensNet.this.siemensHandler);
         }
      });
   }

   public boolean isConnectd() {
      boolean isOk = false;
      if (this.siemensHandler != null) {
         isOk = this.siemensHandler.isConnectd();
      }

      return isOk;
   }

   public void connect() {
      if (!StringUtil.isNullOrEmpty(this.plcIP)) {
         this.connect(this.plcIP);
      }
   }

   public void connect(String host) {
      if (!this.isConnectd()) {
         this.plcIP = host;

         try {
            ChannelFuture channelFuture = this.bootstrap.connect(host, 102);
            channelFuture.awaitUninterruptibly();
            if (!channelFuture.isSuccess()) {
               this.showErrorMsg("连接PLC [" + host + "]超时!");
               return;
            }

            try {
               Thread.sleep(1500L);
            } catch (InterruptedException var4) {
               this.showErrorMsg("connect:" + var4.getMessage());
            }
         } catch (Exception var5) {
            var5.printStackTrace();
         }

      }
   }

   public void close() {
      if (this.eventLoopGroup != null) {
         this.eventLoopGroup.shutdownGracefully();
      }

      log.info("关闭连接,释放对象!");
   }

   public ResultData read(String address) {
      return this.readBytes(address, 0);
   }

   public ResultData readBytes(String address, int count) {
      ResultData rd = new ResultData();
      rd.setConnecd(false);
      rd.setSuccess(false);
      rd.setMessage("当前与PLC通信断开!");
      if (this.isConnectd()) {
         SiemensMessage msg = Utility.getAddress(address);
         if (msg == null) {
            rd.setMessage("请输入正确的PLC地址!");
            return rd;
         }

         try {
            msg.setType(CommandType.Read);
            if (count > 0) {
               if (msg.getDataType() != DataType.Byte) {
                  rd.setSuccess(false);
                  rd.setMessage("对不起,请输入Byte类型地址!");
                  return rd;
               }

               msg.setDataType(DataType.Bytes);
               msg.setDataLength(count);
            } else {
               msg.setDataLength(msg.getDataType().getValue());
            }

            SiemensMessage result = this.send(msg);
            if (result.isSuccess()) {
               Object value = this.getValue(result.getDataValue(), result.getDataType(), result.getDataLength());
               rd.setValue(value);
            }

            rd.setSuccess(result.isSuccess());
            if (this.isConnectd()) {
               rd.setMessage(result.getMessage());
            }

            rd.setConnecd(this.isConnectd());
         } catch (Exception var7) {
            rd.setMessage(var7.getMessage());
         }
      }

      return rd;
   }

   private Object getValue(ByteBuf in, DataType type, int dataLength) {
      Object value = null;
      switch(type) {
      case Bit:
         value = in.readByte() == 1;
         break;
      case Byte:
         value = in.readByte();
         break;
      case Word:
         value = in.readShort();
         break;
      case Dint:
         value = in.readInt();
         break;
      case Long:
         value = in.readLong();
         break;
      case Bytes:
         byte[] buffer = new byte[dataLength];
         in.readBytes(buffer);
         value = buffer;
      }

      ReferenceCountUtil.release(in);
      return value;
   }

   public ResultData write(String address, boolean value) {
      return this.write(address, (byte)(value ? 1 : 0));
   }

   public ResultData write(String address, byte value) {
      ByteBuf buf = Unpooled.buffer(1);
      buf.writeByte(value);
      return this.write(address, buf);
   }

   public ResultData write(String address, short value) {
      ByteBuf buf = Unpooled.buffer(2);
      buf.writeShort(value);
      return this.write(address, buf);
   }

   public ResultData write(String address, int value) {
      ByteBuf buf = Unpooled.buffer(4);
      buf.writeInt(value);
      return this.write(address, buf);
   }

   public ResultData write(String address, long value) {
      ByteBuf buf = Unpooled.buffer(4);
      buf.writeLong(value);
      return this.write(address, buf);
   }

   public ResultData write(String address, byte[] value) {
      ByteBuf buf = Unpooled.wrappedBuffer(value);
      return this.write(address, buf);
   }

   private ResultData write(String address, ByteBuf value) {
      ResultData rd = new ResultData();
      rd.setConnecd(false);
      rd.setSuccess(false);
      rd.setMessage("当前与PLC通信断开!");
      if (this.isConnectd()) {
         rd.setConnecd(true);
         SiemensMessage msg = Utility.getAddress(address);
         if (msg == null) {
            rd.setMessage("当前PLC地址不正确!");
            return rd;
         }

         msg.setType(CommandType.Write);
         msg.setDataValue(value);
         msg.setDataLength(value.writerIndex());

         try {
            SiemensMessage result = this.send(msg);
            if (this.isConnectd()) {
               rd.setMessage(result.getMessage());
            }

            rd.setSuccess(result.isSuccess());
            rd.setValue(result.getDataValue());
            rd.setConnecd(this.isConnectd());
         } catch (Exception var6) {
            rd.setMessage(var6.getMessage());
         }
      }

      return rd;
   }

   private synchronized SiemensMessage send(SiemensMessage message) throws InterruptedException, ExecutionException {
      ChannelPromise promise = this.siemensHandler.sendMessage(message);
      promise.await(5L, TimeUnit.SECONDS);
      return this.siemensHandler.getResponse();
   }

   private void showErrorMsg(String msg) {
      if (!this.errorMsg.equals(msg)) {
         this.errorMsg = msg;
         log.error(this.errorMsg);
      }

   }
}

自定义处理器 SiemensHandler,解码器SiemensDecoder,加码器SiemensEncoder

不便对外提供,可自己实现,这部分可以参考开源框架HslCommunication

PLC连接类SiemensNet 调用流程(伪代码)

public SiemensNet siemensNet;//引入连接类

public void connectPLC() {
   this.siemensNet = new SiemensNet("PLC版本枚举类");//连接类初始化
}

this.siemensNet.connect("127.0.0.1");//传入IP,端口默认是102,可不传

this.siemensNet.read("DB8.DBB1234");//传入PLC点位,读取值

this.siemensNet.write("DB8.DBB1234", 1);//传入PLC点位,写值

Java控制西门子PLC设备有以下方式:

  • HslCommunication,基于S7协议,是个人开源,商业授权的框架,优势是封装了各种类型PLC设备的控制,功能最强大,C#版本更新最快,对C#项目非常友好,Java版本有点跟不上,个人也觉得有点臃肿;
  • OPC UA,基于OPC协议,优势的传输效率高,是工控的标准协议,需要KEPServerEX软件,这个好像是商业的,缺点是KEPServerEX需作为OPC服务器,要配置每个PLC点位,相当于中间层,有点麻烦,目前我还在观望;
  • S7connector,是GitHub上开源的框架,调用方式和Hsl类似,我暂没尝试
  • Socket或Netty,完全自定义实现,缺点是S7协议是加密的,传输需解码加码,是我目前立库项目在用的调用方式,

   -_-个人对这块非常感兴趣,有同方向的老铁们欢迎留言区沟通-_-

使用Netty对接西门子S7协议可以通过以下步骤实现: 1.添加Netty和S7协议库的依赖: ```xml <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.65.Final</version> </dependency> <dependency> <groupId>com.digitalpetri.s7</groupId> <artifactId>s7-protocol</artifactId> <version>2.0.0</version> </dependency> ``` 2.创建S7协议的连接配置: ```java S7ConnectionConfig config = new S7ConnectionConfig.Builder() .withHost("192.168.1.1") // PLC的IP地址 .withRack(0) // 机架号 .withSlot(2) // 插槽号 .build(); ``` 3.创建Netty的Bootstrap: ```java Bootstrap bootstrap = new Bootstrap() .group(new NioEventLoopGroup()) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 添加S7协议的编解码器 pipeline.addLast(new S7ProtocolCodec()); // 添加自定义的ChannelHandler pipeline.addLast(new MyChannelHandler()); } }); ``` 4.连接PLC并发送S7协议请求: ```java ChannelFuture future = bootstrap.connect("192.168.1.1", 102).sync(); Channel channel = future.channel(); // 创建S7协议的读取请求 S7ReadRequest request = new S7ReadRequest.Builder() .addItem(S7AreaDB.DB1, 0, S7WL.BYTE, 10) .build(); // 发送S7协议请求 channel.writeAndFlush(request); ``` 5.自定义ChannelHandler处理S7协议的响应: ```java public class MyChannelHandler extends SimpleChannelInboundHandler<S7Message> { @Override protected void channelRead0(ChannelHandlerContext ctx, S7Message msg) throws Exception { if (msg instanceof S7Response) { S7Response response = (S7Response) msg; // 处理S7协议的响应 } } } ```
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值