diff --git a/web/src/main/java/com/zhehekeji/web/entity/KsecUpPcInfo.java b/web/src/main/java/com/zhehekeji/web/entity/KsecUpPcInfo.java new file mode 100644 index 0000000..7f7ae46 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/entity/KsecUpPcInfo.java @@ -0,0 +1,24 @@ +package com.zhehekeji.web.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 昆船上位机信息 + */ +@Data +@TableName("ksec_up_pc_info") +public class KsecUpPcInfo { + + private Integer id; + + private String ip; + + private Integer port; + + private String name; + + private LocalDateTime createTime; +} diff --git a/web/src/main/java/com/zhehekeji/web/mapper/KsecUpPcMapper.java b/web/src/main/java/com/zhehekeji/web/mapper/KsecUpPcMapper.java new file mode 100644 index 0000000..945d87a --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/mapper/KsecUpPcMapper.java @@ -0,0 +1,7 @@ +package com.zhehekeji.web.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zhehekeji.web.entity.KsecUpPcInfo; + +public interface KsecUpPcMapper extends BaseMapper { +} diff --git a/web/src/main/java/com/zhehekeji/web/pojo/IpInfo.java b/web/src/main/java/com/zhehekeji/web/pojo/IpInfo.java new file mode 100644 index 0000000..e41735e --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/pojo/IpInfo.java @@ -0,0 +1,13 @@ +package com.zhehekeji.web.pojo; + +import lombok.Data; + +@Data +public class IpInfo { + + private Integer id; + + private String ip; + + private Integer port; +} 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 d665f56..72884b2 100644 --- a/web/src/main/java/com/zhehekeji/web/service/InitService.java +++ b/web/src/main/java/com/zhehekeji/web/service/InitService.java @@ -1,10 +1,13 @@ package com.zhehekeji.web.service; import com.zhehekeji.web.entity.Camera; +import com.zhehekeji.web.entity.KsecUpPcInfo; import com.zhehekeji.web.entity.Street; import com.zhehekeji.web.lib.*; import com.zhehekeji.web.mapper.CameraMapper; +import com.zhehekeji.web.mapper.KsecUpPcMapper; import com.zhehekeji.web.mapper.StreetMapper; +import com.zhehekeji.web.service.ksec.KsecNettyClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; @@ -26,34 +29,58 @@ public class InitService implements ApplicationRunner { private StreetMapper streetMapper; @Resource private NettyClient nettyClient; + @Resource + private KsecNettyClient ksecNettyClient; public static Integer cameraNum; @Value("${cameraType}") private Integer cameraType; + @Value("${serverMode}") + private Integer serverMode; + @Resource + private KsecUpPcMapper upPcMapper; + @Override public void run(ApplicationArguments args) throws Exception { //球机登录 List cameras = cameraMapper.selectByMap(new HashMap<>(0)); loginThread loginThread = new loginThread(cameras, cameraType); loginThread.start(); - //plc 连接状态初始化 - List streets = streetMapper.selectByMap(new HashMap<>(0)); + if(serverMode == 0){ + log.info("PLC TCP MODE"); + //plc 连接状态初始化 + List streets = streetMapper.selectByMap(new HashMap<>(0)); + + streets.forEach(street -> { + //随便找一个巷道,只要配置了一个球机,就是单个球机的项目 + if((street.getCamera1Id() != null && street.getCamera2Id() == null)||(street.getCamera2Id() != null && street.getCamera1Id() == null)){ + cameraNum = 1; + } + StreetConn.init(street.getId(),street.getPlcId()); + try { + nettyClient.createClient(street); + }catch (Exception e){ + log.error("streetId:{}:初始plc连接失败,url:{},port:{}",street.getId(),street.getPlcIp(),street.getPlcPort()); + } - streets.forEach(street -> { - //随便找一个巷道,只要配置了一个球机,就是单个球机的项目 - if((street.getCamera1Id() != null && street.getCamera2Id() == null)||(street.getCamera2Id() != null && street.getCamera1Id() == null)){ - cameraNum = 1; - } - StreetConn.init(street.getId(),street.getPlcId()); - try { - nettyClient.createClient(street); - }catch (Exception e){ - log.error("streetId:{}:初始plc连接失败,url:{},port:{}",street.getId(),street.getPlcIp(),street.getPlcPort()); + }); + }else if(serverMode == 1){ + log.info("KESC JSON MODE"); + // 昆船协议 + KsecUpPcInfo ksec = upPcMapper.selectById(1); + if(ksec != null){ + StreetConn.init(1,"ksec"); + try { + ksecNettyClient.createClient(ksec); + }catch (Exception e){ + log.error("kesc connect error,url:{},port:{}",ksec.getIp(),ksec.getPort()); + } + }else { + log.error("ksec no config"); } - - }); + } } diff --git a/web/src/main/java/com/zhehekeji/web/service/MyProtocolDecoder.java b/web/src/main/java/com/zhehekeji/web/service/MyProtocolDecoder.java index 62990fc..43502f1 100644 --- a/web/src/main/java/com/zhehekeji/web/service/MyProtocolDecoder.java +++ b/web/src/main/java/com/zhehekeji/web/service/MyProtocolDecoder.java @@ -59,7 +59,7 @@ public class MyProtocolDecoder extends FixedLengthFrameDecoder { short short6 = in.readShort(); short short7 = in.readShort(); short short8 = in.readShort(); - PlcOrderInfo plcOrderInfo = new PlcOrderInfo(plcId, taskId, (int) short1, (int) short2, (int) short3, (int) short4, (int) short5, (int) short6, (int) short7, (int) short8); + PlcOrderInfo plcOrderInfo = new PlcOrderInfo(plcId, String.valueOf(taskId), (int) short1, (int) short2, (int) short3, (int) short4, (int) short5, (int) short6, (int) short7, (int) short8); byte maohao = in.readByte(); byte leixing = in.readByte(); diff --git a/web/src/main/java/com/zhehekeji/web/service/PlcOrderInfo.java b/web/src/main/java/com/zhehekeji/web/service/PlcOrderInfo.java index 9070718..5ec29a6 100644 --- a/web/src/main/java/com/zhehekeji/web/service/PlcOrderInfo.java +++ b/web/src/main/java/com/zhehekeji/web/service/PlcOrderInfo.java @@ -12,7 +12,7 @@ public class PlcOrderInfo { /** * 任务Id */ - private Integer taskId; + private String taskId; /** * 前两个命令 库内?苦口? 1:库内 2:库口 @@ -56,7 +56,7 @@ public class PlcOrderInfo { private String orderNum; - public PlcOrderInfo(String plcId,Integer taskId, Integer inOut1, Integer leftRight1, Integer column1, Integer row1, Integer inOut2, Integer leftRight2, Integer column2, Integer row2) { + public PlcOrderInfo(String plcId,String taskId, Integer inOut1, Integer leftRight1, Integer column1, Integer row1, Integer inOut2, Integer leftRight2, Integer column2, Integer row2) { this.taskId = taskId; this.inOut1 = inOut1; this.leftRight1 = leftRight1; diff --git a/web/src/main/java/com/zhehekeji/web/service/PlcService.java b/web/src/main/java/com/zhehekeji/web/service/PlcService.java index 258f684..fad00ce 100644 --- a/web/src/main/java/com/zhehekeji/web/service/PlcService.java +++ b/web/src/main/java/com/zhehekeji/web/service/PlcService.java @@ -366,7 +366,7 @@ public class PlcService { String prevFile = mediaPath+stock.getPreoperationPic(); String overFile = mediaPath+stock.getOveroperationPic(); String code = BarCodeScan.detectBestBarCode(overFile); - if(boxNumSys == boxNumAlgo) { + if(boxNumSys == boxNumAlgo && code.equals(barCodeSys)) { stock.setStatus(StockStatus.SUCCESS.getStatus()); log.info("核对正确"); stockMapper.updateById(stock); diff --git a/web/src/main/java/com/zhehekeji/web/service/ksec/KescEncoder.java b/web/src/main/java/com/zhehekeji/web/service/ksec/KescEncoder.java new file mode 100644 index 0000000..b9f6173 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/ksec/KescEncoder.java @@ -0,0 +1,22 @@ +package com.zhehekeji.web.service.ksec; + +import com.alibaba.fastjson.JSONObject; +import com.zhehekeji.web.service.SendHeart; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +import java.nio.charset.StandardCharsets; + +/** + * 昆船JSON版发送 首尾加上 < > + */ +public class KescEncoder extends MessageToByteEncoder { + + + @Override + protected void encode(ChannelHandlerContext channelHandlerContext, KsecInfo ksecInfo, ByteBuf byteBuf) throws Exception { + String body = "<" + JSONObject.toJSONString(ksecInfo) + ">"; + byteBuf.writeBytes(body.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/ksec/KescFilter.java b/web/src/main/java/com/zhehekeji/web/service/ksec/KescFilter.java new file mode 100644 index 0000000..c6223eb --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/ksec/KescFilter.java @@ -0,0 +1,44 @@ +package com.zhehekeji.web.service.ksec; + +import com.zhehekeji.web.service.*; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.timeout.IdleStateHandler; + +import java.util.concurrent.TimeUnit; + +/** + * 客户端过滤器,编解码和心跳的设置 + * + * @author Administrator + * + */ +public class KescFilter extends ChannelInitializer { + + private KsecInfo ksecInfo; + + private PlcService plcService; + + private KsecNettyClient nettyClient; + + public KescFilter(KsecInfo ksecInfo, PlcService plcService, KsecNettyClient nettyClient){ + this.ksecInfo = ksecInfo; + this.plcService = plcService; + this.nettyClient = nettyClient; + + } + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline ph = ch.pipeline(); + //4秒发一次心跳 + ph.addLast(new IdleStateHandler(0, 4, 0, TimeUnit.SECONDS)); + ByteBuf byteBuf = Unpooled.copiedBuffer(">".getBytes()); + ph.addLast(new KsecDecoder(1000,byteBuf,plcService)); + ph.addLast(new KescEncoder()); + ph.addLast(new KescNettyHandler(ksecInfo,nettyClient)); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/ksec/KescNettyHandler.java b/web/src/main/java/com/zhehekeji/web/service/ksec/KescNettyHandler.java new file mode 100644 index 0000000..100ce53 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/ksec/KescNettyHandler.java @@ -0,0 +1,69 @@ +package com.zhehekeji.web.service.ksec; + +import com.zhehekeji.web.service.StreetConn; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleStateEvent; +import lombok.extern.slf4j.Slf4j; + +/** + * 与PLC心跳 处理 + * + * @author Administrator + * + */ +@Slf4j +public class KescNettyHandler extends ChannelInboundHandlerAdapter { + /** 客户端请求的心跳命令 */ + private KsecInfo heart; + + + private KsecNettyClient nettyClient; + + public KescNettyHandler(KsecInfo ksecInfo,KsecNettyClient nettyClient){ + + this.heart = ksecInfo; + this.nettyClient = nettyClient; + } + + /** + * 建立连接时 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + log.info("upPc connected "); + StreetConn.conn(1); + ctx.fireChannelActive(); + } + + /** + * 关闭连接时 + */ + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + log.info("upPc closed"); + StreetConn.disConn(1); + log.info(" streetId reconnect......"); + nettyClient.reconnect(1); + } + + /** + * 心跳请求处理,每4秒发送一次心跳请求; + * + */ + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception { + if (obj instanceof IdleStateEvent) { + log.debug("upPc send heart"); + ctx.channel().writeAndFlush(heart); + IdleStateEvent event = (IdleStateEvent) obj; + +// if (IdleState.WRITER_IDLE.equals(event.state())) { // 如果写通道处于空闲状态就发送心跳命令 +// +// } + } + + } + + +} diff --git a/web/src/main/java/com/zhehekeji/web/service/ksec/KsecDataInfo.java b/web/src/main/java/com/zhehekeji/web/service/ksec/KsecDataInfo.java new file mode 100644 index 0000000..9436151 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/ksec/KsecDataInfo.java @@ -0,0 +1,35 @@ +package com.zhehekeji.web.service.ksec; + +import lombok.Data; + +@Data +public class KsecDataInfo { + + private String SRMNumber; + + private String CmdName; + + private String TaskId; + + private Integer FromAddrType; + + private Integer FromRow; + + private Integer FromBay; + + private Integer FromLevel; + + private Integer ToAddrType; + + private Integer ToRow; + + private Integer ToBay; + + private Integer ToLevel; + + private String code; + + private String WarnCode; + + private Integer AckStatus; +} diff --git a/web/src/main/java/com/zhehekeji/web/service/ksec/KsecDecoder.java b/web/src/main/java/com/zhehekeji/web/service/ksec/KsecDecoder.java new file mode 100644 index 0000000..e7f25c9 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/ksec/KsecDecoder.java @@ -0,0 +1,82 @@ +package com.zhehekeji.web.service.ksec; + +import com.alibaba.fastjson.JSONObject; +import com.zhehekeji.web.service.OrderAction; +import com.zhehekeji.web.service.PlcOrderInfo; +import com.zhehekeji.web.service.PlcService; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.Charset; + +/** + * 昆船通讯协议(TCP 传输JSON) + */ +@Slf4j +public class KsecDecoder extends DelimiterBasedFrameDecoder { + + private PlcService plcService; + + public KsecDecoder(int maxFrameLength, ByteBuf delimiter, PlcService plcService) { + super(maxFrameLength, delimiter); + this.plcService = plcService; + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + in = (ByteBuf) super.decode(ctx, in); + String body = in.toString(Charset.forName("UTF-8")); + if (body.startsWith("<")){ + // 去掉首尾标识符 + body = body.substring(1, body.length()); + KsecInfo ksecInfo = JSONObject.parseObject(body, KsecInfo.class); + log.info("receieve info:{}", ksecInfo); + KsecDataInfo dataInfo = ksecInfo.getData(); + PlcOrderInfo plcOrderInfo = null; + String srmNumber = null; + String cmdName = null; + if(dataInfo != null){ + plcOrderInfo = new PlcOrderInfo(dataInfo.getSRMNumber(), dataInfo.getTaskId(), dataInfo.getFromAddrType(), dataInfo.getFromRow(), dataInfo.getFromBay(), dataInfo.getFromLevel(), dataInfo.getToAddrType(), dataInfo.getToRow(), dataInfo.getToBay(), dataInfo.getToLevel()); + srmNumber = dataInfo.getSRMNumber(); + cmdName = dataInfo.getCmdName(); + } + + if ("A".equals(ksecInfo.getType())) { + //心跳 + } else if ("B".equals(ksecInfo.getType())) { + //任务 + if ("B1".equals(cmdName)) { + plcService.orderStart(plcOrderInfo, srmNumber); + } else if ("B2".equals(cmdName)) { + plcService.orderStop(srmNumber, plcOrderInfo.getOrderNum()); + } + } else if ("C".equals(ksecInfo.getType())) { + //动作 + String code = dataInfo.getCmdName(); + log.info("action code,{} plcId:{},orderInfo:{}", code, srmNumber, plcOrderInfo.toString()); + if (code.equals("C1") || code.equals("C2") || code.equals("C3") || code.equals("C4") || code.equals("C5")) { + // C1-5 是执行动作,需要保存执行到第几步了 + Integer times = OrderAction.put(plcOrderInfo.getOrderNum()); + //执行动作 + plcService.action(plcOrderInfo, srmNumber, times, code); + } else if (code.equals("C7")) { + //旋转360 然后发送C8命令 + plcService.turn360AndC8(srmNumber); + } else { + // 其他命令执行动作 + plcService.action(plcOrderInfo, srmNumber, 1, code); + } + } else if ("D".equals(ksecInfo.getType())) { + //todo 告警 + + } else if ("E".equals(ksecInfo.getType())) { + //盘点 + plcService.recordStock(plcOrderInfo, srmNumber, dataInfo.getCode(), 0, 0); + } + } + in.release(); + return null; + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/ksec/KsecInfo.java b/web/src/main/java/com/zhehekeji/web/service/ksec/KsecInfo.java new file mode 100644 index 0000000..df70245 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/ksec/KsecInfo.java @@ -0,0 +1,23 @@ +package com.zhehekeji.web.service.ksec; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class KsecInfo { + + private String header; + + private String Type; + + private KsecDataInfo data; + + /** + * 心跳格式 + * @return + */ + public static KsecInfo heart(){ + return new KsecInfo("LP","A",null); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/ksec/KsecNettyClient.java b/web/src/main/java/com/zhehekeji/web/service/ksec/KsecNettyClient.java new file mode 100644 index 0000000..4bd4755 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/ksec/KsecNettyClient.java @@ -0,0 +1,93 @@ +package com.zhehekeji.web.service.ksec; + + +import com.zhehekeji.web.entity.KsecUpPcInfo; +import com.zhehekeji.web.mapper.KsecUpPcMapper; +import com.zhehekeji.web.service.PlcService; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; + +@Slf4j +@Component +public class KsecNettyClient { + + private static EventLoopGroup group = new NioEventLoopGroup(); + @Resource + private PlcService plcService; + @Resource + private KsecUpPcMapper ksecUpPcMapper; + + /** + * 重连最大次数 + */ + private static int RECONNECT_NUM = 5; + + private static Channel channel; + + public void createClient(KsecUpPcInfo ksec){ + if (StringUtils.isEmpty(ksec.getIp()) || ksec.getPort() == null) { + return; + } + Bootstrap client = new Bootstrap(); + client.group(group); + client.channel(NioSocketChannel.class); + KsecInfo heart = KsecInfo.heart(); + client.handler(new KescFilter(heart, plcService,this)); + // 连接服务端 + try { + channel = client.connect(ksec.getIp(), ksec.getPort()).sync().channel(); + } catch (InterruptedException e) { + channel = null; + e.printStackTrace(); + throw new RuntimeException(""); + } + } + + /** + * 断线重连 尝试 RECONNECT_NUM 次 + * + * @param upId + */ + public void reconnect(Integer upId) { + Boolean isConnected = false; + int num = 0; + KsecUpPcInfo ksec = ksecUpPcMapper.selectById(upId); + if (ksec == null) { + log.error("reconnect ,upPc is null ,id:{}", upId); + return; + } + while (num < RECONNECT_NUM && !isConnected) { + try { + createClient(ksec); + } catch (Exception e) { + //没连上 继续 + log.error("reconnect error num:{}", num); + num++; + continue; + } + isConnected = true; + } + if (isConnected) { + log.info("plc reconnect success"); + } else { + log.error("plc reconnect error .upPcId:{},reconnect num:{},name:{},ip:{},port:{}", upId, num, ksec.getName(), ksec.getIp(), ksec.getPort()); + } + } + + public static void write(KsecInfo ksecInfo){ + if(channel != null){ + channel.writeAndFlush(ksecInfo); + }else { + log.error(" no connected upPc"); + } + + } +}