Opentcs 夜光开发手册(七)

编程入门 行业动态 更新时间:2024-10-09 12:26:52

Opentcs 夜光开发<a href=https://www.elefans.com/category/jswz/34/1769120.html style=手册(七)"/>

Opentcs 夜光开发手册(七)

夜光序言:

找伴侣,要看的永远是心,一颗善良的心,一颗上进的心,一颗充满责任感的心。远远比金钱更加重要。陪伴优质股成长,我一向认为是最完美的。

不在于未来能够获得多少,在于享受优质股前进的过程。

上图男主挺不错的,虽难人生比较坎坷,最后也收获了幸福

 

 

正文:这一篇聊一聊通讯~~,涉及客户端和服务端的编写,主要服务端是虚拟测试用,客户端则是实际用,实际与AGV对接时候,可以将服务端代码注释掉~~么么哒

package nettybakty.client.plc;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import nettybak.entity.PlcCmd;
import nettybakty.AgvConnectionUtils;
import nettybakty.AgvConstants;
import nettybak.util.HexUtils;
import nettybak.util.PlcCmdFactory;import ioty.bootstrap.Bootstrap;
import ioty.channel.Channel;
import ioty.channel.ChannelFuture;
import ioty.channel.ChannelFutureListener;
import ioty.channel.nio.NioEventLoopGroup;
import ioty.channel.socket.nio.NioSocketChannel;/**  * @Title: PlcClient.java* @Package* @Description: plc通信客户端* @author    Genius  Team* @date*/
public class PlcClient {private Logger logger=LoggerFactory.getLogger(getClass());private NioEventLoopGroup workGroup = new NioEventLoopGroup();private Channel channel;private Bootstrap bootstrap;private final String host;private final int port;private boolean sync=false;//是否同步等待连接创建完成,默认异步public PlcClient(){this(AgvConstants.PLC_DEFAULT_HOST,AgvConstants.PLC_DEFAULT_PORT,false);}public PlcClient(String host, int port,boolean sync){this.host = host;this.port = port;this.sync=sync;}public void start() {try {bootstrap = new Bootstrap();bootstrap.group(workGroup).channel(NioSocketChannel.class).handler(AgvConnectionUtils.plcClientInitializer);if(sync) {ChannelFuture future=bootstrap.connect(host, port).sync();channel=future.channel();addReconnection(future);}else {doConnect();}} catch (Exception e) {throw new RuntimeException(e);}}protected void doConnect() {if (channel != null && channel.isActive()) {logger.debug("夜光:当前链接有活动的channel,不执行重连逻辑");return ;}ChannelFuture future = bootstrap.connect(host, port);addReconnection(future);}public void addReconnection(ChannelFuture future) {future.addListener(new ChannelFutureListener() {public void operationComplete(ChannelFuture futureListener) throws Exception {if (futureListener.isSuccess()) {channel = futureListener.channel();} else {logger.debug("链接失败"+AgvConstants.DEFAULT_REDIRECT_SECOND+"s后执行重连!");futureListener.channel().eventLoop().schedule(new Runnable() {@Overridepublic void run() {doConnect();}}, AgvConstants.DEFAULT_REDIRECT_SECOND, TimeUnit.SECONDS);}}});   	}/*** 本地非集成环境测试方法* 夜光* @throws Exception*/public void test() throws Exception {BufferedReader in = new BufferedReader(new InputStreamReader(System.in));while (true) {String readLine = in.readLine();PlcCmd msg=null;switch(readLine) {case "1" : msg = PlcCmdFactory.getUp(0); break;case "2" : msg = PlcCmdFactory.getDown(0); break;case "0" : msg = PlcCmdFactory.getStopJack(0); break;case "55" : msg = PlcCmdFactory.getInquiryStatus(); break;case "11" : msg = PlcCmdFactory.getForward(); break;case "12" : msg = PlcCmdFactory.getBackWards(); break;case "13" : msg = PlcCmdFactory.getToLeft(); break;case "14" : msg = PlcCmdFactory.getToRight(); break;case "15" : msg = PlcCmdFactory.getLevorotation(); break;case "16" : msg = PlcCmdFactory.getDextrorotation(); break;case "00" : msg = PlcCmdFactory.getStopRun(); break;}if("loop".equals(readLine)) {while(true) {msg=PlcCmdFactory.getUp(0);logger.debug("我是客户端,我准备发送的消息为-升顶:" + HexUtils.toHexString(msg.toByteArray()) );channel.writeAndFlush(msg);Thread.sleep(1000);}}if("loopd".equals(readLine)) {msg=PlcCmdFactory.getStopJack(0);logger.debug("我是客户端,我准备发送的消息为-停止顶升功能:" + HexUtils.toHexString(msg.toByteArray()) );channel.writeAndFlush(msg);while(true) {msg=PlcCmdFactory.getDown(0);logger.debug("我是客户端,我准备发送的消息为-落顶:" + HexUtils.toHexString(msg.toByteArray()) );channel.writeAndFlush(msg);Thread.sleep(1000);}}if("loopr".equals(readLine)) {while(true) {msg=PlcCmdFactory.getInquiryStatus();logger.debug("我是客户端,我准备发送的消息为-问询:" + HexUtils.toHexString(msg.toByteArray()) );channel.writeAndFlush(msg);Thread.sleep(1000);}}logger.debug("我是客户端,我准备发送的消息为:" + HexUtils.toHexString(msg.toByteArray()) );channel.writeAndFlush(msg);}}public void writeAndFlush(PlcCmd msg) {logger.debug("我是客户端,我准备发送的消息为:" + HexUtils.toHexString(msg.toByteArray()) );channel.writeAndFlush(msg);}public static void main(String[] args) throws Exception {PlcClient client = new PlcClient(AgvConstants.PLC_DEFAULT_HOST,AgvConstants.PLC_DEFAULT_PORT,true);client.start();client.test();}}

 

package nettybakty.client.plc;import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import nettybak.entity.PlcResult;
import nettybak.util.CRC16;
import nettybak.util.HexUtils;import ioty.buffer.ByteBuf;
import ioty.channel.ChannelHandlerContext;
import ioty.handler.codec.ByteToMessageDecoder;
import ioty.util.ByteProcessor.IndexOfProcessor;/**  * @Package* @Description: plc通信解码器-解码Plc反馈的信息* @author Genius  Team* @date*/
public class PlcClientDecoder extends ByteToMessageDecoder {private static final Logger logger=LoggerFactory.getLogger(PlcClientDecoder.class);/*** 解码PLC返回的数据.* <p>该方法执行结束,如果没有新的可读数据,即ridx==widx,则会释放该字节缓冲区资源* @param ctx* @param in* @param out* @throws Exception* @see ioty.handler.codec.ByteToMessageDecoder#decode(ioty.channel.ChannelHandlerContext, ioty.buffer.ByteBuf, List)*/@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {//-----TEST STARTin.markReaderIndex();int len=in.readableBytes();byte[] read=new byte[len];in.readBytes(read);logger.trace("decode解码PLC反馈的数据,接受到的数据字节长度为: {} ,值为:{}",len,HexUtils.toHexString(read));    in.resetReaderIndex();//-----TEST END//夜光:按照 站号和功能码 组合做头部分帧int numIdx=in.forEachByte(new IndexOfProcessor((byte) 0x01));//在当前可读字节中寻找"站号"第一次出现的位置[数据过滤,认为站号之前的未读数据为垃圾数据]if(numIdx==-1) {return;//没有找到则return继续等待新内容写入}in.readerIndex(numIdx);//将当前读索引设置为站号所在位置if (in.readableBytes() < PlcResult.BASE_LEN) {return;//以当前站号为起始,可读数据小于头部固定长度(站号+功能码+字节总数),则return继续等待写入}byte typeCode=in.getByte(in.readerIndex()+1);//以不推进readerIndex的方式获取到当前指令对应的功能码.(功能码固定位于指令开头的索引+1处)//如果在对应位置读取不到正确的功能码,则认为当前位置不是正确的头部位置,以推进readIndex的方式,丢弃该位置和该位置以前的数据if(typeCode!=0x03) {in.readerIndex(in.readerIndex()+1+1);//in.readerIndex()+1只包括该位置以前的数据,所以要再加一次1,把该位置的也丢弃return;}byte dataLength=in.getByte(in.readerIndex()+2);//以不推进readerIndex的方式获取到当前指令对应的字节总数.(字节总数固定位于指令开头的索引+2处)if(in.readableBytes()<PlcResult.BASE_LEN+dataLength+PlcResult.CHECK_LEN) {return;//以当前站号为起始,可读数据不小于头部固定长度(站号+功能码+字节总数),但可读数据小于当前指令的字节总数,则return继续等待写入}//所有条件都符合,尝试读取封装数据PlcResult result= packPlcResult(in);//如果result不为空,则调用out.add(result); 数据会进入信道的下一个处理器,if(result!=null) {out.add(result);}}/*** 将plc反馈的字节数据封装为对应的实体对象* <p>如果封装完成后的对象,校验和与计算出的校验和不匹配,则认为数据在传输中出现错误,则返回null* @param in* @return*/public PlcResult packPlcResult(ByteBuf in) {PlcResult plcResult=new PlcResult();plcResult.setCode(in.readByte());plcResult.setTypeCode(in.readByte());plcResult.setDataLength(in.readByte());byte[] datas=new byte[plcResult.getDataLength()];in.readBytes(datas);plcResult.setData(datas);plcResult.setChecksum(in.readShort());short ck=(short)CRC16.calcCrc16(plcResult.toNoCheckByteArray());if(ck!=0&&ck==plcResult.getChecksum()) {return plcResult;}else {return null;}}}
package nettybakty.client.plc;import nettybak.entity.PlcCmd;import ioty.buffer.ByteBuf;
import ioty.channel.ChannelHandlerContext;
import ioty.handler.codec.MessageToByteEncoder;/**  * @Package* @Description: plc通信编码器-编码发送给PLC的指令* @author  Genius  Team* @date*/
public class PlcClientEncoder extends MessageToByteEncoder<PlcCmd>  {@Overrideprotected void encode(ChannelHandlerContext ctx, PlcCmd cmd, ByteBuf out) throws Exception {byte[] bytes=cmd.toByteArray();out.writeBytes(bytes);}}

 

package nettybakty.client.plc;import nettybakty.AgvConstants;
import nettybakty.client.BaseChannelInitializer;
import nettybakty.client.BaseClientHandler;import ioty.channel.ChannelPipeline;
import ioty.channel.socket.SocketChannel;
import ioty.handler.timeout.IdleStateHandler;/**  * @Title: PlcClientInitializer.java* @Package* @Description: plc通信处理器初始化* @author    Genius   Team* @date*/
public class PlcClientInitializer extends BaseChannelInitializer<SocketChannel> {public PlcClientInitializer(BaseClientHandler<?> handler) {super(handler);}@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();// pipeline.addLast(new IdleStateHandler(AgvConstants.DEFAULT_HEARTBEAT_SECOND, 0, 0));//心跳pipeline.addLast("decoder", new PlcClientDecoder());pipeline.addLast("encoder", new PlcClientEncoder());
//	        pipeline.addLast("handler", this.getBusinessHandler());}}

 

 

一般客户端和服务端都是差不多几个Class串在一起嗯~~

 

package nettybakty.client.plc;import java.InetSocketAddress;
import java.util.concurrent.ConcurrentLinkedQueue;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import nettybak.entity.AgvEntity;
import nettybak.entity.AgvSession;
import nettybak.entity.PlcCmd;
import nettybak.entity.PlcCommand;
import nettybak.entity.PlcResult;
import nettybakty.AgvUtils;
import nettybak.util.HexUtils;import ioty.channel.Channel;
import ioty.channel.ChannelHandler.Sharable;
import ioty.channel.ChannelHandlerContext;/**  * @Title: PlcDefaultClientHandler.java* @Package* @Description: plc通信默认业务处理器* @author   Genius  Team* @date*/
@Sharable
public class PlcDefaultClientHandler extends PlcBaseClientHandler {private Logger logger=LoggerFactory.getLogger(getClass());/*** 处理下发plc指令* @param ctx* @param s
//	 * @see */@Overrideprotected void businessHandler(ChannelHandlerContext ctx, PlcResult s) {//接收到消息后触发onReadMsg事件AgvEntity entity=new AgvEntity();
//		entity.setPlcResult(s);
//		onReadMsg(entity);Channel channel=ctx.channel();//目前channelKey为远程主机的ip,根据具体情况,channelKey可以在消息体中携带(除外,不支持)InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress();String remoteIP = insocket.getAddress().getHostAddress();//logger.debug("我是客户端,当前链接的IP为:"+remoteIP);//System.out.println("SYSOUT-我是客户端,我接收到的消息为:"+s);logger.debug("我是plc客户端,当前链接的plc-IP为:{}我接收到的消息为:{}",remoteIP,HexUtils.toHexString(s.toByteArray()));}/*** * * 测试方法-链接建立成功后发送一条指令* @param ctx* @throws Exception
//	 * @see netty.client.plc.PlcBaseClientHandler#channelActive(ioty.channel.ChannelHandlerContext)*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {// TODO Auto-generated method stubsuper.channelActive(ctx);PlcCmd cmd=new PlcCmd();//ctx.writeAndFlush(cmd);}}

 

 

package nettybakty.client.plc;import java.InetSocketAddress;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import nettybak.entity.PlcResult;
import nettybakty.AgvConnectionUtils;
import nettybakty.client.BaseClientHandler;
import nettybak.util.Q600CmdFactory;import ioty.channel.ChannelHandlerContext;
import ioty.handler.timeout.IdleStateEvent;/**  * @Title: PlcBaseClientHandler.java* @Package* @Description: plc通信业务处理器基类* @author   Genius  Team* @date*/
public abstract class PlcBaseClientHandler extends  BaseClientHandler<PlcResult> {private Logger logger=LoggerFactory.getLogger(getClass());/*** 嗯唔:需要实现的处理业务逻辑的方法*/protected abstract void businessHandler(ChannelHandlerContext ctx, PlcResult s);@Overrideprotected void channelRead0(ChannelHandlerContext ctx, PlcResult s) throws Exception {businessHandler(ctx, s);}/*** 心跳处理* @param ctx* @param evt* @throws Exception* @see ioty.channel.ChannelInboundHandlerAdapter#userEventTriggered(ioty.channel.ChannelHandlerContext, Object)*/@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {// IdleStateHandler 所产生的 IdleStateEvent 的处理逻辑.if (evt instanceof IdleStateEvent) {IdleStateEvent e = (IdleStateEvent) evt;switch (e.state()) {case READER_IDLE:handleReaderIdle(ctx);break;case WRITER_IDLE:handleWriterIdle(ctx);break;case ALL_IDLE:handleAllIdle(ctx);break;default:break;}}}/*** 信道被激活(每当成功建立链接时,信道会被激活)* @param ctx* @throws Exception* @see ioty.channel.ChannelInboundHandlerAdapter#channelActive(ioty.channel.ChannelHandlerContext)*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {logger.debug("---" + ctx.channel().remoteAddress() + " 信道被激活---");}/*** 断开自动重连 * @param ctx* @throws Exception* @see ioty.channel.ChannelInboundHandlerAdapter#channelInactive(ioty.channel.ChannelHandlerContext)*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {super.channelInactive(ctx);logger.debug("---" + ctx.channel().remoteAddress() + " is inactive---连接中断开,执行重连逻辑");InetSocketAddress addr=(InetSocketAddress)ctx.channel().remoteAddress();String ip=addr.getAddress().getHostAddress();int port=addr.getPort();AgvConnectionUtils.connectPlc(ip, port, false);}/**处理指定的时间间隔内没有读到数据* @param ctx*/protected void handleReaderIdle(ChannelHandlerContext ctx) {logger.debug("---READER_IDLE---指定的时间间隔内没有读到agv返回的数据,准备发送 问询坐标 指令");//到了指定的间隔时间,就问询一次坐标ctx.channel().writeAndFlush(Q600CmdFactory.Q600_GET_POSE);logger.debug("---READER_IDLE--发送问询坐标指令完成");}/**处理指定的时间间隔内没有写入数据* @param ctx*/protected void handleWriterIdle(ChannelHandlerContext ctx) {logger.debug("---WRITER_IDLE---指定的时间间隔内没有写入数据");}/**处理指定的时间间隔内没有读/写入数据* @param ctx*/protected void handleAllIdle(ChannelHandlerContext ctx) {logger.debug("---ALL_IDLE---指定的时间间隔内没有读/写入数据");}
}

 

 

总结:实际测试只要编写上面客户端就可以了嗯,主要就是与PLC之间的编解码问题

 

 

 

 

更多推荐

Opentcs 夜光开发手册(七)

本文发布于:2024-02-27 13:41:58,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1706762.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:手册   Opentcs

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!