#
whycq
2024-08-16 5bdad72f5d5077ca875dd03cfdaafb3d7aba93da
#
5个文件已修改
6个文件已添加
438 ■■■■■ 已修改文件
app/build.gradle 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/MainActivity.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/action/AGV_11_UP.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/action/AckMsgBuilder.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/action/AgvCompleteType.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/action/CommonConstant.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/action/RedisConstant.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/met/AbstractInboundHandler.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/protocol/AGV_A1_DOWN.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/socket/NettyServer.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/agvcontroller/socket/NettyServerHandler.java 89 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build.gradle
@@ -37,6 +37,7 @@
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    implementation 'org.greenrobot:eventbus:3.2.0'
    implementation 'redis.clients:jedis:3.0.1'
    implementation 'androidx.recyclerview:recyclerview:1.3.0'
    implementation 'io.netty:netty-all:4.1.68.Final'
app/src/main/java/com/example/agvcontroller/MainActivity.java
@@ -23,10 +23,16 @@
import com.example.agvcontroller.utils.SnowflakeIdWorker;
import java.net.Socket;
import java.sql.Time;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
public class MainActivity extends AppCompatActivity {
    public static final Map<String, Object> map = new ConcurrentHashMap();
    private RecyclerView recyclerView;
    private ItemAdapter itemAdapter;
@@ -45,22 +51,46 @@
    private boolean isDowm = false;
    private boolean isOpen = false;
    String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16);
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (isDowm) {
                String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16);
                AgvAction agvAction = new AgvAction<>(ForceSwitchAction.class)
                        .setAgvNo("1")
                        .setSerialNo(substring)
                        .setVal(1)
                        .bodySync((action) -> action.setPwd((short) 21));
                nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端
                handler.sendEmptyMessageDelayed(0, 100);
                boolean result = false;
                long timestamp = System.currentTimeMillis();
                while (System.currentTimeMillis() - timestamp < 5000) {
                    Object o = map.get(substring);
                    if (null != o) {
                        result = true;
                        map.remove(o);
                        break;
                    }
                }
                if (result) {
                    // alert ok
                } else {
                    throw new TimeoutException("超时");
                }
            }
            if (isOpen) {
                String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16);
                AgvAction agvAction = new AgvAction<>(ForceSwitchAction.class)
                        .setAgvNo("12")
                        .setSerialNo(substring)
@@ -99,7 +129,7 @@
                if (vibrator != null && vibrator.hasVibrator()) {
                    vibrator.vibrate(500);
                }
                String substring = String.valueOf(new SnowflakeIdWorker().nextId()).substring(0,16);
                AgvAction agvAction = new AgvAction<>(HandOutAction.class)
                        .setAgvNo("12")
                        .setSerialNo(substring)
app/src/main/java/com/example/agvcontroller/action/AGV_11_UP.java
New file
@@ -0,0 +1,115 @@
package com.example.agvcontroller.action;
import com.example.agvcontroller.protocol.IMessageBody;
import com.example.agvcontroller.protocol.Utils;
import com.example.agvcontroller.socket.RadixTools;
import java.io.Serializable;
/**
 * 动作完成数据包
 *
 * Created by vincent on 2023/3/21
 */
public class AGV_11_UP implements IMessageBody, Serializable {
    private static final long serialVersionUID = 8403019742104020004L;
    @Override
    public byte[] writeToBytes() {
        return new byte[0];
    }
    @Override
    public void readFromBytes(byte[] bytes) {
//        try {
            this.serialNo = new String(Utils.sliceWithReverse(bytes, 0, 16));
//        } catch (UnsupportedEncodingException e) {
//            e.printStackTrace();
//        }
        this.completeCode = Utils.slice(bytes, 16, 1)[0];
        this.completeType = AgvCompleteType.query(this.completeCode);
        this.qrCode = Utils.zeroFill(String.valueOf(RadixTools.bytesToInt(Utils.sliceWithReverse(bytes, 17, 4))), CommonConstant.QR_CODE_LEN);
        this.direction = RadixTools.byteToShort(Utils.sliceWithReverse(bytes, 21,2));
//        this.locCode = new String(Utils.sliceWithReverse(bytes, 23, 16)).replaceAll("\\u0000", "");
//        this.boxCode = new String(Utils.sliceWithReverse(bytes, 39, 16)).replaceAll("\\u0000", "");
        this.locCode = Utils.zeroFill(String.valueOf(RadixTools.bytesToInt(Utils.sliceWithReverse(bytes, 23, 4))), CommonConstant.QR_CODE_LEN);
        this.boxCode = Utils.zeroFill(String.valueOf(RadixTools.bytesToInt(Utils.sliceWithReverse(bytes, 39, 4))), CommonConstant.QR_CODE_LEN);
    }
    private String serialNo;
    // 完成动作码
    private int completeCode;
    private AgvCompleteType completeType;
    // 地面码 - 8位
    private String qrCode;
    // 车头朝向
    private short direction;
    // 货位二维码
    private String locCode;
    public String getSerialNo() {
        return serialNo;
    }
    public void setSerialNo(String serialNo) {
        this.serialNo = serialNo;
    }
    public int getCompleteCode() {
        return completeCode;
    }
    public void setCompleteCode(int completeCode) {
        this.completeCode = completeCode;
    }
    public AgvCompleteType getCompleteType() {
        return completeType;
    }
    public void setCompleteType(AgvCompleteType completeType) {
        this.completeType = completeType;
    }
    public String getQrCode() {
        return qrCode;
    }
    public void setQrCode(String qrCode) {
        this.qrCode = qrCode;
    }
    public short getDirection() {
        return direction;
    }
    public void setDirection(short direction) {
        this.direction = direction;
    }
    public String getLocCode() {
        return locCode;
    }
    public void setLocCode(String locCode) {
        this.locCode = locCode;
    }
    public String getBoxCode() {
        return boxCode;
    }
    public void setBoxCode(String boxCode) {
        this.boxCode = boxCode;
    }
    // 料箱二维码
    private String boxCode;
}
app/src/main/java/com/example/agvcontroller/action/AckMsgBuilder.java
New file
@@ -0,0 +1,44 @@
package com.example.agvcontroller.action;
import com.example.agvcontroller.protocol.AgvPackage;
import com.example.agvcontroller.protocol.ProtocolType;
import com.example.agvcontroller.protocol.ProtocolUtils;
import java.util.logging.Logger;
import io.netty.buffer.ByteBuf;
/**
 * 应答报文建造者
 * Created by vincent on 2019-04-04
 */
public class AckMsgBuilder {
//    private static final Logger log = LoggerFactory.getLogger(AckMsgBuilder.class);
    /**
     * 成功应答
     * @return ByteBuf
     */
    public static AgvPackage ofSuccess(AgvPackage original, ProtocolType ackType) {
        return ProtocolUtils.installDownProtocol(
                original.getHeader().getUniqueNo()
                , ackType
        );
    }
    /**
     * 失败应答
     * @return ByteBuf
     */
    public static ByteBuf ofFail(ByteBuf sourceBuff) {
        return sourceBuff;
    }
}
app/src/main/java/com/example/agvcontroller/action/AgvCompleteType.java
New file
@@ -0,0 +1,53 @@
package com.example.agvcontroller.action;
/**
 * Agv动作完成类型
 */
public enum AgvCompleteType {
    COMMON_COMPLETE(0x01, "初始化自检或被动查询完成"),
    STRAIGHT_COMPLETE(0x02, "直行路径完成"),
    TURN_CORNER_COMPLETE(0x03, "转弯完成"),
    RETURN_ZERO_OF_ROTATE_TELESCOPIC_COMPLETE(0x04, "旋转伸缩归零完成"),
    TAKE_FROM_STORAGE_COMPLETE(0x05, "从料仓取货完成"),
    RELEASE_FROM_STORAGE_COMPLETE(0x06, "往料仓放货完成"),
    TAKE_FROM_SHELVES_COMPLETE(0x07, "从货架取货完成"),
    RELEASE_FROM_SHELVES_COMPLETE(0x08, "往货架放货完成"),
    SCAN_COMPLETE(0x09, "扫描货架和料箱完成"),
    DOCKING_CHARGE_COMPLETE(0x0C, "充电对接完成"),
    FULL_CHARGE_COMPLETE(0x0D, "充满电"),
    STOP_RECENT_CODE_COMPLETE(0x0E, "停到最近二维码完成"),
    ENTIRE_PATH_COMPLETE(0x0F, "整个路径执行完成"),
    ;
    private int code;
    private String desc;
    AgvCompleteType(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    public static AgvCompleteType query(int completeCode) {
        for (AgvCompleteType type : AgvCompleteType.values()) {
            if (completeCode == type.code) {
                return type;
            }
        }
        return null;
    }
}
app/src/main/java/com/example/agvcontroller/action/CommonConstant.java
New file
@@ -0,0 +1,10 @@
package com.example.agvcontroller.action;
/**
 * Created by vincent on 2023/3/29
 */
public class CommonConstant {
    public static final int QR_CODE_LEN = 8;
}
app/src/main/java/com/example/agvcontroller/action/RedisConstant.java
New file
@@ -0,0 +1,38 @@
package com.example.agvcontroller.action;
/**
 * Created by vincent on 2023/3/14
 */
public class RedisConstant {
    public static final String AGV_CMD_DOWN_FLAG = "AGV_CMD_DOWN_FLAG";
    public static final String AGV_CMD_UP_FLAG = "AGV_CMD_UP_FLAG";
    public static final String AGV_ONLINE_FLAG = "AGV_ONLINE_FLAG";
    public static final String AGV_COMPLETE_FLAG = "AGV_COMPLETE_FLAG";
    public static final String AGV_DATA_FLAG = "AGV_DATA_FLAG";
    public static final String AGV_FAULT_REPORT_FLAG = "AGV_FAULT_REPORT_FLAG";
    public static final String DIGITAL_AGV_FLAG = "DIGITAL_AGV_FLAG";
    public static final int CMD_TIMEOUT_LIMIT = 5000;
    public static final String AGV_MAP_ASTAR_FLAG = "AGV_MAP_ASTAR_FLAG";
    public static final String AGV_MAP_ASTAR_CODE_FLAG = "AGV_MAP_ASTAR_CODE_FLAG";
    public static final String AGV_MAP_ASTAR_TURN_FLAG = "AGV_MAP_ASTAR_TURN_FLAG";
    public static final String AGV_MAP_ASTAR_CDA_FLAG = "AGV_MAP_ASTAR_CDA_FLAG";
    public static final String AGV_MAP_ASTAR_DYNAMIC_FLAG = "AGV_MAP_ASTAR_DYNAMIC_FLAG";
    public static final String AGV_MAP_ASTAR_WAVE_FLAG = "AGV_MAP_ASTAR_WAVE_FLAG";
    public static final String AGV_TRAFFIC_JAM_FLAG = "AGV_TRAFFIC_JAM_FLAG";
}
app/src/main/java/com/example/agvcontroller/met/AbstractInboundHandler.java
New file
@@ -0,0 +1,36 @@
package com.example.agvcontroller.met;
import com.example.agvcontroller.protocol.AgvPackage;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
/**
 * netty handler增强器
 * 设计模式: 适配器模式
 * Created by vincent on 2019-04-06
 */
public abstract class AbstractInboundHandler<T> extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
        @SuppressWarnings("unchecked")
        T t = (T) obj;
        if (channelRead0(ctx, t)) {
            ctx.fireChannelRead(t);
        } else  {
            // 管道中断,fireChannelRead未执行,需要手动释放堆外内存
            if (obj instanceof AgvPackage){
                AgvPackage pac = (AgvPackage) obj;
                if (pac.getSourceBuff().refCnt() > 0) {
                    pac.getSourceBuff().skipBytes(pac.getSourceBuff().readableBytes());
                    ReferenceCountUtil.release(pac.getSourceBuff());
                }
            }
        }
    }
    protected abstract boolean channelRead0(ChannelHandlerContext ctx, T t) throws Exception;
}
app/src/main/java/com/example/agvcontroller/protocol/AGV_A1_DOWN.java
@@ -21,6 +21,22 @@
    }
    public byte getAckSign() {
        return ackSign;
    }
    public void setAckSign(byte ackSign) {
        this.ackSign = ackSign;
    }
    public byte[] getTemp() {
        return temp;
    }
    public void setTemp(byte[] temp) {
        this.temp = temp;
    }
    private byte ackSign = 0x11;
    private byte[] temp = new byte[4];
app/src/main/java/com/example/agvcontroller/socket/NettyServer.java
@@ -35,7 +35,7 @@
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new NettyServerHandler()).addLast(new ProtocolEncoder());
                            ch.pipeline().addLast(new ProtocolEncoder()).addLast(new ProtocolDecoder(4096)).addLast(new NettyServerHandler());
                        }
                    })
//                    .addLast(new NettyServerHandler())
app/src/main/java/com/example/agvcontroller/socket/NettyServerHandler.java
@@ -1,30 +1,34 @@
package com.example.agvcontroller.socket;
import java.nio.charset.StandardCharsets;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import android.util.Log;
import com.example.agvcontroller.Item;
import com.example.agvcontroller.MainActivity;
import com.example.agvcontroller.action.AGV_11_UP;
import com.example.agvcontroller.action.AckMsgBuilder;
import com.example.agvcontroller.met.AbstractInboundHandler;
import com.example.agvcontroller.protocol.AGV_A1_DOWN;
import com.example.agvcontroller.protocol.AgvAction;
import com.example.agvcontroller.protocol.AgvPackage;
import com.example.agvcontroller.protocol.ProtocolType;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
public class NettyServerHandler  extends AbstractInboundHandler<AgvPackage> {
    private static final String TAG = "NettyServerHandler";
    private static ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
@@ -42,22 +46,53 @@
        Log.d(TAG, "Client disconnected: " + clientId);
    }
//    @Override
//    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        // 处理接收到的消息
//        ByteBuf byteBuf = (ByteBuf) msg;
//        try {
//            while (byteBuf.isReadable()) {
//                byte[] bytes = new byte[byteBuf.readableBytes()];
//                byteBuf.readBytes(bytes);
//                String hexString = bytesToHex(bytes);
//                // 获取agv信息 添加到list中
//                Log.d(TAG, "ctx: " + ctx.channel().remoteAddress().toString() );
//                Log.d(TAG, "Received: " + hexString);
//            }
//        } finally {
//            byteBuf.release();
//        }
//    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 处理接收到的消息
        ByteBuf byteBuf = (ByteBuf) msg;
        try {
            while (byteBuf.isReadable()) {
                byte[] bytes = new byte[byteBuf.readableBytes()];
                byteBuf.readBytes(bytes);
                String hexString = bytesToHex(bytes);
                // 获取agv信息 添加到list中
                Log.d(TAG, "ctx: " + ctx.channel().remoteAddress().toString() );
                Log.d(TAG, "Received: " + hexString);
            }
        } finally {
            byteBuf.release();
    protected boolean channelRead0(ChannelHandlerContext ctx, AgvPackage pac) throws Exception {
        String serialNum = pac.getHeader().getSerialNum();
        // ack
        ProtocolType ackType = isNeedAck(pac);
        final String uniqueNo = pac.getHeader().getUniqueNo();
        label : switch (pac.getHeader().getProtocolType()){
            case ACTION_COMPLETE:   // 动作完成数据包
                AGV_11_UP agv_11_up = (AGV_11_UP) pac.getBody().getMessageBody();
//                redis.push(RedisConstant.AGV_COMPLETE_FLAG, AgvProtocol.build(uniqueNo).setMessageBody(agv_11_up));
                // 动作完成应答
                if (null != ackType) {
                    AgvPackage ackPac = AckMsgBuilder.ofSuccess(pac, ackType);
                    AGV_A1_DOWN agv_a1_down = (AGV_A1_DOWN) ackPac.getBody().getMessageBody();
                    agv_a1_down.setAckSign((byte) agv_11_up.getCompleteCode());
                    ctx.writeAndFlush(ackPac);
                }
                MainActivity.map.put(serialNum, Boolean.TRUE);
                break label;
        }
        return false;
    }
    private String bytesToHex(byte[] bytes) {
@@ -109,4 +144,18 @@
        ctx.close();
    }
    /**
     * 服务器是否需要应答
     */
    public static ProtocolType isNeedAck(AgvPackage pac) {
        switch (pac.getHeader().getProtocolType()) {
            case ACTION_COMPLETE:
                return ProtocolType.ACTION_SUCCESS_ACK;
            case LOGIN_REPORT:
                return ProtocolType.LOGIN_ACK;
            default:
                return null;
        }
    }
}