From 73f8dee58ba4d1d3d87a794de3e62f94c411fe7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E9=B8=A3?= Date: Fri, 15 Jan 2021 18:10:33 +0800 Subject: [PATCH] add tcp --- .../web/controller/PLCController.java | 40 ++++++++++ .../java/com/zhehekeji/web/entity/Street.java | 4 + .../com/zhehekeji/web/service/ClientMap.java | 37 ++++++++++ .../web/service/HeartNettyClientFilter.java | 34 +++++++++ .../web/service/HeartNettyClientHandler.java | 73 +++++++++++++++++++ .../zhehekeji/web/service/InitService.java | 3 + .../web/service/MyProtocolEncoder.java | 26 +++++++ .../com/zhehekeji/web/service/SendHeart.java | 26 +++++++ .../zhehekeji/web/service/StreetService.java | 15 +++- 9 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 web/src/main/java/com/zhehekeji/web/controller/PLCController.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/ClientMap.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/HeartNettyClientFilter.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/HeartNettyClientHandler.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/MyProtocolEncoder.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/SendHeart.java diff --git a/web/src/main/java/com/zhehekeji/web/controller/PLCController.java b/web/src/main/java/com/zhehekeji/web/controller/PLCController.java new file mode 100644 index 0000000..85c5226 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/controller/PLCController.java @@ -0,0 +1,40 @@ +package com.zhehekeji.web.controller; + +import com.github.pagehelper.PageInfo; +import com.zhehekeji.core.pojo.Result; +import com.zhehekeji.core.util.Assert; +import com.zhehekeji.web.entity.Street; +import com.zhehekeji.web.mapper.StreetMapper; +import com.zhehekeji.web.pojo.street.StreetSearch; +import com.zhehekeji.web.pojo.street.StreetVO; +import com.zhehekeji.web.service.ClientMap; +import com.zhehekeji.web.service.StreetService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +@Api(value = "PLCController",tags = "PLC管理") +@RestController(value = "PLCController") +@RequestMapping("/plc") +public class PLCController { + + @Resource + private StreetService streetService; + @Resource + private StreetMapper streetMapper; + + + @GetMapping("/heart") + @ApiOperation(value = "心跳") + public Result heart(@RequestParam Integer id) throws InterruptedException { + Street street = streetMapper.selectById(id); + Assert.isTrue(street!= null && street.getPlcIp() != null && street.getPlcPort() != null,"未配置IP"); + ClientMap.createClient(street); + return Result.success(); + } + + + +} diff --git a/web/src/main/java/com/zhehekeji/web/entity/Street.java b/web/src/main/java/com/zhehekeji/web/entity/Street.java index 9fd591a..774b38f 100644 --- a/web/src/main/java/com/zhehekeji/web/entity/Street.java +++ b/web/src/main/java/com/zhehekeji/web/entity/Street.java @@ -19,6 +19,10 @@ public class Street { @ApiModelProperty("PLC标识") private String plcId; + private String plcIp; + + private Integer plcPort; + @ApiModelProperty("左货架类型 0:单伸 1:双伸") private Integer leftType; diff --git a/web/src/main/java/com/zhehekeji/web/service/ClientMap.java b/web/src/main/java/com/zhehekeji/web/service/ClientMap.java new file mode 100644 index 0000000..fde68ee --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/ClientMap.java @@ -0,0 +1,37 @@ +package com.zhehekeji.web.service; + +import com.zhehekeji.core.util.Assert; +import com.zhehekeji.web.entity.Street; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class ClientMap { + + private static Map channelFutureMap = new HashMap<>(); + + private static EventLoopGroup group = new NioEventLoopGroup(); + + public static void createClient(Street street) throws InterruptedException { + Bootstrap client = new Bootstrap(); + // 第1步 定义线程组,处理读写和链接事件,没有了accept事件 + client.group(group); + // 第2步 绑定客户端通道 + client.channel(NioSocketChannel.class); + // 第3步 给NIoSocketChannel初始化handler, 处理读写事件 + client.handler(new HeartNettyClientFilter(street.getId())); + // 连接服务端 + client.connect(street.getPlcIp(), street.getPlcPort()).sync().channel(); + } + + public static void close() { + group.shutdownGracefully(); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/HeartNettyClientFilter.java b/web/src/main/java/com/zhehekeji/web/service/HeartNettyClientFilter.java new file mode 100644 index 0000000..8b116df --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/HeartNettyClientFilter.java @@ -0,0 +1,34 @@ +package com.zhehekeji.web.service; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.timeout.IdleStateHandler; + +import java.util.concurrent.TimeUnit; + +/** + * 客户端过滤器,如编解码和心跳的设置 + * + * @author Administrator + * + */ +public class HeartNettyClientFilter extends ChannelInitializer { + + private Integer streetId; + + public HeartNettyClientFilter(Integer streetId){ + this.streetId = streetId; + } + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline ph = ch.pipeline(); + //因为服务端设置的超时时间是5秒,所以客户端设置4秒 + ph.addLast(new IdleStateHandler(0, 4, 0, TimeUnit.SECONDS)); + //处理客户端的业务逻辑 + ph.addLast(new HeartNettyClientHandler(streetId)); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/HeartNettyClientHandler.java b/web/src/main/java/com/zhehekeji/web/service/HeartNettyClientHandler.java new file mode 100644 index 0000000..59840e5 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/HeartNettyClientHandler.java @@ -0,0 +1,73 @@ +package com.zhehekeji.web.service; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import io.netty.util.CharsetUtil; +import lombok.extern.slf4j.Slf4j; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 处理客户端业务逻辑:心跳超时处理、服务端返回的数据处理 + * + * @author Administrator + * + */ +@Slf4j +public class HeartNettyClientHandler extends ChannelInboundHandlerAdapter { + /** 客户端请求的心跳命令 */ + private static final SendHeart s = new SendHeart(); + + private Integer streetId; + + public HeartNettyClientHandler(Integer streetId){ + this.streetId = streetId; + } + + /** + * 建立连接时 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.fireChannelActive(); + } + + /** + * 关闭连接时 + */ + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + log.error("streetId:{}连接被关闭",streetId); + } + + /** + * 心跳请求处理,每4秒发送一次心跳请求; + * + */ + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception { + log.debug("streetId:{}心跳",streetId); + + if (obj instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent) obj; + if (IdleState.WRITER_IDLE.equals(event.state())) { // 如果写通道处于空闲状态就发送心跳命令 + ctx.channel().writeAndFlush(s); + } + } + + } + + /** + * 业务逻辑处理 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + log.debug("streetId:{},client receive msg:{}",streetId,msg.toString()); + + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/InitService.java b/web/src/main/java/com/zhehekeji/web/service/InitService.java index c5f0617..535ef52 100644 --- a/web/src/main/java/com/zhehekeji/web/service/InitService.java +++ b/web/src/main/java/com/zhehekeji/web/service/InitService.java @@ -5,6 +5,7 @@ import com.zhehekeji.web.lib.CameraConnMap; import com.zhehekeji.web.lib.LoginModule; import com.zhehekeji.web.lib.NetSDKLib; import com.zhehekeji.web.mapper.CameraMapper; +import com.zhehekeji.web.mapper.StreetMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; @@ -23,6 +24,8 @@ public class InitService implements ApplicationRunner { private CameraMapper cameraMapper; @Resource private TcpListener tcpListener; + @Resource + private StreetMapper streetMapper; @Override public void run(ApplicationArguments args) throws Exception { diff --git a/web/src/main/java/com/zhehekeji/web/service/MyProtocolEncoder.java b/web/src/main/java/com/zhehekeji/web/service/MyProtocolEncoder.java new file mode 100644 index 0000000..f6c3c04 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/MyProtocolEncoder.java @@ -0,0 +1,26 @@ +package com.zhehekeji.web.service; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +import java.nio.charset.Charset; + +public class MyProtocolEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, SendHeart msg, ByteBuf out) throws Exception { + if(msg == null){ + throw new Exception("msg is null"); + } + out.writeChar(msg.getL()); + out.writeChar(msg.getP()); + out.writeShort(msg.getLength()); + out.writeCharSequence(msg.getPlcId(), Charset.defaultCharset()); + out.writeCharSequence(msg.getType(), Charset.defaultCharset()); + out.writeCharSequence(msg.getId(),Charset.defaultCharset()); + out.writeChar(msg.getMaohao()); + out.writeChar(msg.getH()); + out.writeBytes(msg.getEmp()); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/SendHeart.java b/web/src/main/java/com/zhehekeji/web/service/SendHeart.java new file mode 100644 index 0000000..4d84355 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/SendHeart.java @@ -0,0 +1,26 @@ +package com.zhehekeji.web.service; + +import lombok.Data; + +@Data +public class SendHeart { + + private char l = 'L'; + + private char p = 'P'; + + private short length = 43; + + private String plcId = "ABC000"; + + private String type = "0A"; + + private String id = "0101010101"; + + private char maohao = ':'; + + private char h = 'H'; + + private byte[]emp = new byte[]{10}; + +} diff --git a/web/src/main/java/com/zhehekeji/web/service/StreetService.java b/web/src/main/java/com/zhehekeji/web/service/StreetService.java index 748c924..f84aaa9 100644 --- a/web/src/main/java/com/zhehekeji/web/service/StreetService.java +++ b/web/src/main/java/com/zhehekeji/web/service/StreetService.java @@ -3,6 +3,7 @@ package com.zhehekeji.web.service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; +import com.zhehekeji.core.util.Assert; import com.zhehekeji.web.entity.Street; import com.zhehekeji.web.entity.StreetShelve; import com.zhehekeji.web.mapper.OrderLastMediaMapper; @@ -12,6 +13,7 @@ import com.zhehekeji.web.pojo.street.StreetSearch; import com.zhehekeji.web.pojo.street.StreetType; import com.zhehekeji.web.pojo.street.StreetVO; import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; @@ -40,7 +42,12 @@ public class StreetService { List shelves = check(street); street.setCreateTime(LocalDateTime.now()); street.setUpdateTime(LocalDateTime.now()); - streetMapper.insert(street); + try { + streetMapper.insert(street); + }catch (DuplicateKeyException e){ + Assert.isTrue(false,"PLC ID已存在"); + } + streetShelve(shelves,street.getId()); return street.getId(); } @@ -90,7 +97,11 @@ public class StreetService { @Transactional(rollbackFor = Exception.class) public void edit(Street street){ List shelves = check(street); - streetMapper.updateById(street); + try { + streetMapper.updateById(street); + }catch (DuplicateKeyException e){ + Assert.isTrue(false,"PLC ID已存在"); + } delStreetShelve(street.getId()); streetShelve(shelves,street.getId()); }