.idea/deploymentTargetSelector.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/StartActivity.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/protocol/ProtocolDecoder.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/socket/NettyServer.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/res/layout/activity_main.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@ <selectionStates> <SelectionState runConfigName="app"> <option name="selectionMode" value="DROPDOWN" /> <DropdownSelection timestamp="2024-08-15T08:33:10.282255800Z"> <Target type="DEFAULT_BOOT"> <handle> <DeviceId pluginId="PhysicalDevice" identifier="serial=2MM0223916052360" /> </handle> </Target> </DropdownSelection> <DialogSelection /> </SelectionState> </selectionStates> </component> app/src/main/java/com/example/agvcontroller/StartActivity.java
@@ -52,7 +52,7 @@ } }); socketManager = new SocketManager(); socketManager.startServer(8022); socketManager.startServer(8080); } @Subscribe(threadMode = ThreadMode.MAIN) app/src/main/java/com/example/agvcontroller/protocol/ProtocolDecoder.java
New file @@ -0,0 +1,158 @@ package com.example.agvcontroller.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.util.ReferenceCountUtil; import com.example.agvcontroller.socket.RadixTools; import java.net.InetSocketAddress; import java.util.List; /** * Created by vincent on 2019-04-10 */ public class ProtocolDecoder extends ByteToMessageDecoder { private final int maxFrameLength; public ProtocolDecoder(int maxFrameLength){ this.maxFrameLength = maxFrameLength; } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { String ip = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress(); // log.info("ip:{} ===>> {}", ip, ByteBufUtil.hexDump(in).toUpperCase()); int startMark = indexOfStartMark(in); if (startMark == -1){ in.skipBytes(in.readableBytes()); return; } if (in.readableBytes() > maxFrameLength) { in.skipBytes(in.readableBytes()); return; } // 去除无用前缀报文 if (startMark != 0){ in.readerIndex(startMark); in.discardReadBytes(); } // 粘包处理 in.markReaderIndex(); in.readerIndex(PackagePart.CONTENT_LENGTH.getStartIndex()); int pacTotalLen = PackagePart.START_SYMBOL.getLen() + PackagePart.CONTENT_LENGTH.getLen() + in.readShortLE() + PackagePart.VALIDE_CODE.getLen() ; in.resetReaderIndex(); ByteBuf byteBuf = in.readSlice(pacTotalLen); // 生成和初始化消息包装类 AgvPackage pac = AgvPackage.valueOfEmpty(); pac.setSourceBuff(byteBuf); // 解析 out.add(analyzeProtocol(pac)); // byteBuf.resetReaderIndex(); // tip: 如果 ridx == widx,则 refCnt = 0 in.resetReaderIndex(); in.skipBytes(pacTotalLen); // tip: This method will be called till either the input has nothing to read,无语! ReferenceCountUtil.retain(in); // 解码器自动释放 refCnt - 1 } public AgvPackage analyzeProtocol(AgvPackage pac){ ByteBuf byteBuf = pac.getSourceBuff(); PacHeader pacHeader = pac.getHeader(); // 唯一标识码 byteBuf.readerIndex(PackagePart.UNIQUENO.getStartIndex()); byte[] bytes = new byte[PackagePart.UNIQUENO.getLen()]; byteBuf.readBytes(bytes); Utils.reverse(bytes); String uniqueno = String.valueOf(RadixTools.bytesToInt(bytes)); if (!Cools.isEmpty(uniqueno)) { pacHeader.setUniqueNo(uniqueno); byteBuf.resetReaderIndex(); } else { return pac.setErrorPac(Boolean.TRUE).setPacErrorType(PacErrorType.PAC_ANALYZE_ERROR); } // 获取标识符 byte startSymbol = byteBuf.readByte(); DirectionType direction = null; if ((0xEE&0xFF) == (startSymbol&0xFF)) { pacHeader.setStartSymbol(startSymbol); direction = DirectionType.DOWN; } else if ((0xAA&0xFF) == (startSymbol&0xFF)) { pacHeader.setStartSymbol(startSymbol); direction = DirectionType.UP; } else { return pac.setErrorPac(Boolean.TRUE).setPacErrorType(PacErrorType.PAC_ANALYZE_ERROR); } // 获取协议体长度 int contentLength = byteBuf.readShortLE(); if (contentLength >= 0) { pacHeader.setContentLength(contentLength); } else { return pac.setErrorPac(Boolean.TRUE).setPacErrorType(PacErrorType.PAC_ANALYZE_ERROR); } // skip uniqueNo byteBuf.readerIndex(byteBuf.readerIndex() + PackagePart.UNIQUENO.getLen()); // 获取时间戳 byteBuf.readerIndex(PackagePart.TIMESTAMP.getStartIndex()); int timestamp = byteBuf.readIntLE(); if (timestamp > 0) { pacHeader.setTimestamp(timestamp); } else { return pac.setErrorPac(Boolean.TRUE).setPacErrorType(PacErrorType.PAC_ANALYZE_ERROR); } // 获取命令标识 int commandByte = byteBuf.readByte() & 0xFF; ProtocolType protocolType = ProtocolType.getByCode(commandByte, direction); if (null != protocolType) { pacHeader.setProtocolType(protocolType); } else { return pac.setErrorPac(Boolean.TRUE).setPacErrorType(PacErrorType.PAC_ANALYZE_ERROR); } // 获取协议体 if (contentLength > 0) { ByteBuf body = byteBuf.readSlice(pac.getHeader().getContentLength() - ( + PackagePart.UNIQUENO.getLen() + PackagePart.TIMESTAMP.getLen() + PackagePart.COMMAND_MARK.getLen() )); if (null != body && body.readableBytes() >= 0) { pac.getBody().setContent(body); } else { return pac.setErrorPac(Boolean.TRUE).setPacErrorType(PacErrorType.PAC_ANALYZE_ERROR); } } // 校验码 if(byteBuf.readableBytes() == 2) { pac.setValidCode(byteBuf.readUnsignedShortLE()); } else { return pac.setErrorPac(Boolean.TRUE).setPacErrorType(PacErrorType.PAC_ANALYZE_ERROR); } return pac; } // 获取标识位下标 private int indexOfStartMark(ByteBuf inputBuffer){ int length = inputBuffer.writerIndex(); // 报文长度至少大于2 if (length < 2) { return -1; } int readerIndex = inputBuffer.readerIndex(); for(int i = readerIndex; i < length - 1; i ++) { byte b1 = inputBuffer.getByte(i); if ((0xEE&0xFF) == (b1&0xFF) || (0xAA&0xFF) == (b1&0xFF)) { return i; } } return -1; } } app/src/main/java/com/example/agvcontroller/socket/NettyServer.java
@@ -1,5 +1,6 @@ package com.example.agvcontroller.socket; import com.example.agvcontroller.protocol.ProtocolDecoder; import com.example.agvcontroller.protocol.ProtocolEncoder; import io.netty.bootstrap.ServerBootstrap; @@ -37,6 +38,7 @@ ch.pipeline().addLast(new NettyServerHandler()).addLast(new ProtocolEncoder()); } }) // .addLast(new NettyServerHandler()) // .childHandler(new ChannelInitializer<Channel>(){ // // @Override app/src/main/res/layout/activity_main.xml
@@ -99,10 +99,10 @@ android:backgroundTint="#2196F3" /> <com.google.android.material.button.MaterialButton android:id="@+id/btn_force_switch" android:id="@+id/btn_extend_fork" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="手动" android:text="伸叉" android:layout_marginLeft="20sp" android:layout_centerVertical="true" android:layout_alignParentLeft="true" @@ -112,6 +112,22 @@ android:minWidth="10dp" android:layout_alignLeft="@+id/btn_rotatopn_left" android:layout_alignBottom="@+id/btn_go_forward" android:backgroundTint="#2196F3" /> <com.google.android.material.button.MaterialButton android:id="@+id/btn_collect_fork" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="收叉" android:layout_marginLeft="20sp" android:layout_centerVertical="true" android:layout_alignParentLeft="true" app:cornerRadius="5dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:minWidth="10dp" android:layout_alignLeft="@+id/btn_rotatopn_left" android:layout_alignBottom="@+id/btn_go_backward" android:backgroundTint="#2196F3" /> </RelativeLayout> @@ -127,6 +143,82 @@ android:layout_height="0dp" android:layout_weight="1"> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center"> <com.google.android.material.button.MaterialButton android:id="@+id/btn_model_switch" android:layout_width="wrap_content" android:layout_height="40dp" android:text="手动模式" android:layout_centerHorizontal="true" app:cornerRadius="5dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:minWidth="10dp" android:textSize="10sp" android:backgroundTint="#2196F3" /> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center"> <com.google.android.material.button.MaterialButton android:id="@+id/btn_force_switch" android:layout_width="wrap_content" android:layout_height="40dp" android:text="开启强制" android:layout_centerHorizontal="true" app:cornerRadius="5dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:minWidth="10dp" android:textSize="10sp" android:backgroundTint="#2196F3" /> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center"> <com.google.android.material.button.MaterialButton android:id="@+id/btn_single_switch" android:layout_width="wrap_content" android:layout_height="40dp" android:text="单轴使能" android:layout_centerHorizontal="true" app:cornerRadius="5dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:minWidth="10dp" android:textSize="10sp" android:backgroundTint="#2196F3" /> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center"> <com.google.android.material.button.MaterialButton android:id="@+id/btn_function_switch" android:layout_width="wrap_content" android:layout_height="40dp" android:text="功能切换" android:layout_centerHorizontal="true" app:cornerRadius="5dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:minWidth="10dp" android:textSize="10sp" android:backgroundTint="#2196F3" /> </LinearLayout> </LinearLayout>