#
whycq
2024-08-16 dbeb3dd2e88d28da73ebc1f6843355fb51e5603f
#
4个文件已修改
1个文件已添加
266 ■■■■■ 已修改文件
.idea/deploymentTargetSelector.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/StartActivity.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/protocol/ProtocolDecoder.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/socket/NettyServer.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_main.xml 96 ●●●●● 补丁 | 查看 | 原始文档 | 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>