| | |
| | | import com.example.agvcontroller.action.HandOutAction; |
| | | import com.example.agvcontroller.socket.NettyServerHandler; |
| | | import com.example.agvcontroller.socket.SocketManager; |
| | | import com.example.agvcontroller.utils.DialogUtil; |
| | | 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 { |
| | |
| | | private Button rotatopnLeftBtn; // 左旋转 |
| | | private Button goForwardBtn; // 前进 |
| | | private Button goBackwardBtn; // 后退 |
| | | private Button modelSwitchBtn; // 进入、退出 手动 |
| | | private Socket socket; |
| | | |
| | | SocketManager socketManager; |
| | |
| | | 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) { |
| | |
| | | forceSwitchBtn = findViewById(R.id.btn_force_switch); |
| | | goForwardBtn = findViewById(R.id.btn_go_forward); |
| | | goBackwardBtn = findViewById(R.id.btn_go_backward); |
| | | modelSwitchBtn = findViewById(R.id.btn_model_switch); |
| | | |
| | | Intent intent = getIntent(); |
| | | clientId = intent.getStringExtra("message"); |
| | | Log.i("message1",clientId); |
| | | |
| | | // 进出手动模式 |
| | | // 强制模式开关 |
| | | forceSwitchBtn.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | |
| | | nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 |
| | | } |
| | | }); |
| | | |
| | | // 强制模式开关 |
| | | modelSwitchBtn.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | | 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) |
| | | .setVal(1) |
| | | .bodySync((action) -> action.setPwd((short) 21)); |
| | | nettyServerHandler.sendMessageToClient(clientId, agvAction); // 发送消息到客户端 |
| | | |
| | | |
| | | boolean result = false; |
| | | long timestamp = System.currentTimeMillis(); |
| | | |
| | | Log.i("substring",substring); |
| | | while (System.currentTimeMillis() - timestamp < 20000) { |
| | | |
| | | Object o = map.get(substring); |
| | | |
| | | if (null != o) { |
| | | result = true; |
| | | map.remove(o); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (result) { |
| | | // alert ok 修改按钮样式 名称 |
| | | modelSwitchBtn.setText("自动模式"); |
| | | } else { |
| | | // throw new TimeoutException("超时"); |
| | | DialogUtil.showAlertDialog(MainActivity.this, "提示", "请求应答超时!"); |
| | | } |
| | | } |
| | | }); |
| | | stopBtn.setOnTouchListener(new CarTouchButton()); |
| | | goForwardBtn.setOnTouchListener(new CarTouchButton()); |
| | | goBackwardBtn.setOnTouchListener(new CarTouchButton()); |
| | |
| | | } |
| | | }); |
| | | socketManager = new SocketManager(); |
| | | socketManager.startServer(8080); |
| | | socketManager.startServer(8081); |
| | | } |
| | | |
| | | @Subscribe(threadMode = ThreadMode.MAIN) |
| | |
| | | |
| | | } |
| | | |
| | | public String getSerialNo() { |
| | | return serialNo; |
| | | } |
| | | // 流水号 - 16 |
| | | private String serialNo; |
| | | |
| | |
| | | package com.example.agvcontroller.protocol; |
| | | |
| | | |
| | | import com.example.agvcontroller.action.AgvCompleteType; |
| | | import com.example.agvcontroller.action.CommonConstant; |
| | | import com.example.agvcontroller.socket.RadixTools; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | |
| | | |
| | | @Override |
| | | public void readFromBytes(byte[] messageBodyBytes) { |
| | | try { |
| | | this.serialNo = new String(Utils.sliceWithReverse(messageBodyBytes, 0, 16)); |
| | | |
| | | this.cmdCode = Utils.slice(messageBodyBytes, 16, 1)[0]; |
| | | } catch (Exception e) { |
| | | |
| | | } |
| | | } |
| | | |
| | | // 流水号 - 16 |
New file |
| | |
| | | package com.example.agvcontroller.protocol; |
| | | |
| | | import com.example.agvcontroller.action.AgvCompleteType; |
| | | import com.example.agvcontroller.action.CommonConstant; |
| | | import com.example.agvcontroller.socket.RadixTools; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * 动作完成数据包 |
| | | * |
| | | * Created by vincent on 2023/3/21 |
| | | */ |
| | | public class AGV_02_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; |
| | | |
| | | } |
| | |
| | | |
| | | } |
| | | |
| | | public String getSerialNo() { |
| | | return ""; |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | } |
| | | |
| | | public String getSerialNo() { |
| | | return ""; |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | private byte[] temp = new byte[4]; |
| | | |
| | | public String getSerialNo() { |
| | | return ""; |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | private byte[] temp = new byte[4]; |
| | | |
| | | public String getSerialNo() { |
| | | return ""; |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | |
| | | void readFromBytes(byte[] messageBodyBytes); |
| | | |
| | | String getSerialNo(); |
| | | |
| | | } |
New file |
| | |
| | | package com.example.agvcontroller.protocol; |
| | | |
| | | import com.example.agvcontroller.met.AbstractInboundHandler; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import io.netty.buffer.ByteBuf; |
| | | import io.netty.channel.ChannelHandler; |
| | | import io.netty.channel.ChannelHandlerContext; |
| | | |
| | | /** |
| | | * 报文主体handler |
| | | * Created by vincent on 2023-03-15 |
| | | */ |
| | | //@Component("messageBodyHandler") |
| | | @ChannelHandler.Sharable |
| | | public class MessageBodyHandler extends AbstractInboundHandler<AgvPackage> { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(MessageBodyHandler.class); |
| | | |
| | | @Override |
| | | @SuppressWarnings("all") |
| | | protected boolean channelRead0(ChannelHandlerContext ctx, AgvPackage pac) { |
| | | |
| | | String namespace = IMessageBody.class.getPackage().getName(); |
| | | |
| | | ProtocolType protocolType = pac.getHeader().getProtocolType(); |
| | | |
| | | |
| | | if (null == protocolType) { |
| | | |
| | | // 未知包 |
| | | return Boolean.FALSE; |
| | | } |
| | | |
| | | String className = namespace + ".AGV_" + Utils.zeroFill(Integer.toHexString(protocolType.getCode()).toUpperCase(), 2) |
| | | +"_" |
| | | |
| | | + pac.getHeader().getProtocolType().getDirection().toString().toUpperCase(); |
| | | |
| | | Class<?> cls = null; |
| | | try { |
| | | |
| | | |
| | | cls = Class.forName(className);; |
| | | |
| | | } catch (ClassNotFoundException e) { |
| | | |
| | | log.error("MessageBodyHandler ClassNotFoundException", e);log.error("找不到指定类:{}", className); |
| | | |
| | | return Boolean.FALSE; |
| | | } |
| | | |
| | | Object obj = null; |
| | | |
| | | if (null != cls) { |
| | | |
| | | |
| | | try { |
| | | obj = cls.newInstance(); |
| | | |
| | | } catch (ReflectiveOperationException reflectiveOperationException) { |
| | | log.error("MessageBodyHandler ReflectiveOperationException", reflectiveOperationException); |
| | | |
| | | log.error("java反射创建实例失败:{}", className); |
| | | |
| | | // 。。。 |
| | | return Boolean.FALSE; |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | ByteBuf contentBuf = pac.getBody().getContent(); contentBuf.markReaderIndex(); // sign |
| | | // body.buf 属于切片获取, slice 与 channel中的缓冲区 为同一 refenec |
| | | |
| | | |
| | | |
| | | contentBuf.resetReaderIndex(); |
| | | |
| | | int len= contentBuf.readableBytes(); |
| | | |
| | | byte[] bytes = new byte[len]; |
| | | |
| | | |
| | | |
| | | pac.getBody().getContent().readBytes(bytes); |
| | | |
| | | |
| | | |
| | | // 读取字节数组 ===>> 实际报文类型 |
| | | IMessageBody iMessageBody = null; |
| | | |
| | | if (null != obj) { |
| | | |
| | | if ( obj instanceof IMessageBody) { |
| | | |
| | | iMessageBody = (IMessageBody) obj; |
| | | |
| | | |
| | | iMessageBody.readFromBytes(bytes); |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | pac.getBody().setMessageBody(iMessageBody); contentBuf.resetReaderIndex(); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | COMMAND_MARK(11, 1, "命令标识"), |
| | | |
| | | /** |
| | | * 流水号 |
| | | */ |
| | | SERIAL_NUM(13, 16, "流水号"), |
| | | |
| | | /** |
| | | * 数据单元 |
| | | */ |
| | | CONTENT(12, -1, "数据单元"), |
| | |
| | | package com.example.agvcontroller.socket; |
| | | |
| | | import com.example.agvcontroller.protocol.MessageBodyHandler; |
| | | import com.example.agvcontroller.protocol.ProtocolDecoder; |
| | | import com.example.agvcontroller.protocol.ProtocolEncoder; |
| | | |
| | |
| | | .childHandler(new ChannelInitializer<SocketChannel>() { |
| | | @Override |
| | | public void initChannel(SocketChannel ch) throws Exception { |
| | | ch.pipeline().addLast(new ProtocolEncoder()).addLast(new ProtocolDecoder(4096)).addLast(new NettyServerHandler()); |
| | | ch.pipeline().addLast(new ProtocolEncoder()) |
| | | .addLast(new ProtocolDecoder(4096)) |
| | | .addLast(new MessageBodyHandler()) |
| | | .addLast(new NettyServerHandler()); |
| | | } |
| | | }) |
| | | // .addLast(new NettyServerHandler()) |
| | |
| | | @Override |
| | | protected boolean channelRead0(ChannelHandlerContext ctx, AgvPackage pac) throws Exception { |
| | | |
| | | String serialNum = pac.getHeader().getSerialNum(); |
| | | String serialNum = pac.getBody().getMessageBody().getSerialNo(); |
| | | Log.i("substring",serialNum); |
| | | MainActivity.map.put(serialNum, Boolean.TRUE); |
| | | // ack |
| | | ProtocolType ackType = isNeedAck(pac); |
| | | final String uniqueNo = pac.getHeader().getUniqueNo(); |
| | |
| | | ctx.writeAndFlush(ackPac); |
| | | } |
| | | |
| | | MainActivity.map.put(serialNum, Boolean.TRUE); |
| | | |
| | | |
| | | break label; |
| | | } |
New file |
| | |
| | | package com.example.agvcontroller.utils; |
| | | |
| | | import android.app.AlertDialog; |
| | | import android.content.Context; |
| | | import android.content.DialogInterface; |
| | | |
| | | public class DialogUtil { |
| | | |
| | | public static void showAlertDialog(Context context, String title, String message) { |
| | | AlertDialog.Builder builder = new AlertDialog.Builder(context); |
| | | builder.setTitle(title) |
| | | .setMessage(message) |
| | | .setPositiveButton("确定", new DialogInterface.OnClickListener() { |
| | | @Override |
| | | public void onClick(DialogInterface dialog, int which) { |
| | | dialog.dismiss(); |
| | | } |
| | | }) |
| | | .setNegativeButton("取消", new DialogInterface.OnClickListener() { |
| | | @Override |
| | | public void onClick(DialogInterface dialog, int which) { |
| | | dialog.dismiss(); |
| | | } |
| | | }) |
| | | .show(); |
| | | } |
| | | } |