14 files modified
2 files deleted
1 files renamed
| | |
| | | return AjaxResult.success(state); |
| | | } |
| | | |
| | | @ApiOperation("无人机控制订阅") |
| | | @GetMapping("/uavControlSubscribe") |
| | | public AjaxResult uavControlSubscribe(String boxSn) { |
| | | tyService.uavControlSubscribe(boxSn); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | @ApiOperation("云台转动(基于角度)") |
| | | @GetMapping("/controlAngle") |
| | | public AjaxResult controlAngle(String boxSn,Integer actionId,Integer rotationAngle) { |
| | |
| | | public abstract class BaseCommand { |
| | | private short dataHeader; // 帧头 |
| | | private short dataLength; // 数据长度 |
| | | private byte[] boxSn; // 云盒编号 |
| | | private byte cmdCode; // 指令编号 |
| | | |
| | | // 编码方法,子类需要实现 |
| | |
| | | |
| | | @Data |
| | | public class ControlAngleCommand extends BaseCommand{ |
| | | private final byte[] boxSn; // 云盒编号 |
| | | private final byte encryptFlag; // 加密标志 |
| | | private final byte actionId; // 动作编号 |
| | | private final byte actionCode; // 动作编号 |
| | | private final byte rotationAngle; // 转动角度 |
| | | |
| | | public ControlAngleCommand(byte[] boxSn, byte encryptFlag, byte actionId, byte rotationAngle) { |
| | | if (boxSn.length != 15) { |
| | | throw new IllegalArgumentException("云盒编号长度必须为15字节"); |
| | | } |
| | | public ControlAngleCommand(String boxSn, byte actionCode, byte rotationAngle) { |
| | | setDataHeader((short)0x7479); |
| | | setDataLength(getDataLength()); |
| | | setDataLength((short)19); |
| | | setBoxSn(boxSn.getBytes()); |
| | | setCmdCode((byte)0xD1); |
| | | this.boxSn = boxSn; |
| | | this.encryptFlag = encryptFlag; |
| | | this.actionId = actionId; |
| | | |
| | | this.encryptFlag = 0x00; |
| | | this.actionCode = actionCode; |
| | | this.rotationAngle = rotationAngle; |
| | | } |
| | | |
| | | public short getDataLength() { |
| | | // 计算数据长度: 云盒编号 + 指令编号 + 加密标志 + 动作编号 + 转动角度 |
| | | return (short) (1 + 15 + 1 + 1 + 1); // 帧头 + 数据内容 |
| | | } |
| | | |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); // 写入帧头 |
| | | out.writeShort(getDataLength()); // 写入数据长度 |
| | | out.writeBytes(boxSn); // 写入云盒编号 |
| | | out.writeByte(getCmdCode()); // 写入指令编号 |
| | | out.writeByte(encryptFlag); // 写入加密标志 |
| | | out.writeByte(actionId); // 写入动作编号 |
| | | out.writeByte(actionCode); // 写入动作编号 |
| | | out.writeByte(rotationAngle); // 写入转动角度 |
| | | } |
| | | } |
| | |
| | | private byte actionCode; |
| | | private TelemetryDataPB.UavInitInfo uavInitInfo; |
| | | |
| | | public DroneReadyNotificationCommand(TelemetryDataPB.UavInitInfo uavInitInfo, byte actionCode) { |
| | | public DroneReadyNotificationCommand(String boxSn,TelemetryDataPB.UavInitInfo uavInitInfo, byte actionCode) { |
| | | setDataHeader((short) 0x6A77); // 设置帧头 |
| | | // setDataLength((short) (1 + uavState.toByteArray().length)); // 数据长度 |
| | | setBoxSn(boxSn.getBytes()); |
| | | setCmdCode((byte) 0xD1); // 设置命令编号 |
| | | |
| | | this.uavInitInfo = uavInitInfo; |
| | | this.actionCode = actionCode; |
| | | } |
| | |
| | | this.timestamp = timestamp; |
| | | } |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); |
| | | out.writeShort(getDataLength()); |
| | | out.writeByte(getCmdCode()); |
| | | out.writeLong(timestamp); // 写入时间戳 |
| | | } |
| | | } |
| | |
| | | private long timestamp; // 时间戳 |
| | | |
| | | public HeartbeatReplyCommand(long timestamp) { |
| | | this.timestamp = timestamp; |
| | | setDataHeader((short) 0x6A77); // 设置帧头 |
| | | setCmdCode((byte) 0x02); // 设置指令编号 |
| | | setDataLength((short) (13)); // 数据长度 (指令编号 + 时间戳) |
| | | this.timestamp = timestamp; |
| | | } |
| | | |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); |
| | | out.writeShort(getDataLength()); |
| | | out.writeByte(getCmdCode()); |
| | | out.writeLong(timestamp); // 写入时间戳 |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); // 写入帧头 |
| | | out.writeShort(getDataLength()); // 写入数据长度 |
| | | out.writeByte(getCmdCode()); // 写入指令编号 |
| | | |
| | | out.writeDouble(longitude); // 写入经度 |
| | | out.writeDouble(latitude); // 写入纬度 |
| | | out.writeFloat(height); // 写入高度 |
| | |
| | | |
| | | @Override |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); // 写入帧头 |
| | | out.writeShort(getDataLength()); // 写入数据长度 |
| | | out.writeByte(getCmdCode()); // 写入指令编号 |
| | | out.writeInt(userId); // 写入用户编号 |
| | | out.writeBytes(accessToken); // 写入访问令牌 |
| | | } |
| | |
| | | |
| | | @Override |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); // 写入帧头 |
| | | out.writeShort(getDataLength()); // 写入数据长度 |
| | | out.writeByte(getCmdCode()); // 写入指令编号 |
| | | out.writeByte(success); // 写入用户编号 |
| | | } |
| | | } |
| | |
| | | public class UavInitInfoCommand extends BaseCommand{ |
| | | |
| | | private final byte encryptFlag; // 加密标志 |
| | | private final byte actionId; // 动作编号 |
| | | private final byte actionCode; // 动作编号 |
| | | private final byte result; // 执行结果 |
| | | |
| | | public UavInitInfoCommand(byte encryptFlag, byte actionId, byte result) { |
| | | public UavInitInfoCommand(String boxSn, byte actionCode) { |
| | | setDataHeader((short)0x7479); |
| | | setDataLength((short) 3); |
| | | setDataLength((short) 18); |
| | | setBoxSn(boxSn.getBytes()); |
| | | setCmdCode((byte)0xD1); |
| | | this.encryptFlag = encryptFlag; |
| | | this.actionId = actionId; |
| | | this.result = result; |
| | | this.encryptFlag = 0x00; |
| | | this.actionCode = actionCode; |
| | | this.result = 0x01; |
| | | } |
| | | |
| | | public void encode(ByteBuf out) { |
| | | out.writeShort(getDataHeader()); // 写入帧头 |
| | | out.writeShort(getDataLength()); // 写入数据长度 |
| | | out.writeByte(getCmdCode()); // 写入指令编号 |
| | | out.writeByte(encryptFlag); // 写入加密标志 |
| | | out.writeByte(actionId); // 写入动作编号 |
| | | out.writeByte(actionCode); // 写入动作编号 |
| | | out.writeByte(result); // 写入执行结果 |
| | | } |
| | | } |
| | |
| | | break; |
| | | case 0x03: |
| | | case 0x04: |
| | | byte[] boxSn = new byte[dataLength - 1]; |
| | | in.readBytes(boxSn); |
| | | SubscribeReplyCommand subscribeReply = new SubscribeReplyCommand(new String(boxSn)); |
| | | log.debug("订阅回复: {}", subscribeReply.getBoxSn()); |
| | | break; |
| | | case (byte) 0xA8: |
| | | byte[] uavStatus = new byte[dataLength - 1]; |
| | |
| | | in.readBytes(uavData1); |
| | | byte[] uavData2 = new byte[15]; |
| | | in.readBytes(uavData2); |
| | | String boxSn = new String(uavData2); |
| | | TelemetryDataPB.UavInitInfo uavInitInfo = TelemetryDataPB.UavInitInfo.parseFrom(uavData1); |
| | | baseCommand = new DroneReadyNotificationCommand(uavInitInfo, actionCode); |
| | | baseCommand = new DroneReadyNotificationCommand(boxSn, uavInitInfo, actionCode); |
| | | log.info("无人机初始化数据: {}", uavInitInfo.getUavModel()); |
| | | break; |
| | | default: |
| | |
| | | import io.netty.buffer.ByteBuf; |
| | | import io.netty.channel.ChannelHandlerContext; |
| | | import io.netty.handler.codec.MessageToByteEncoder; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | @Slf4j |
| | | public class MessageProtocolEncoder extends MessageToByteEncoder<BaseCommand> { |
| | | @Override |
| | | protected void encode(ChannelHandlerContext ctx, BaseCommand command, ByteBuf out) throws Exception { |
| | | out.writeShort(command.getDataHeader()); // 写入帧头 |
| | | out.writeShort(command.getDataLength()); // 写入数据长度 |
| | | if(command.getBoxSn() != null) { |
| | | out.writeBytes(command.getBoxSn()); |
| | | }// 写入指令编号 |
| | | out.writeByte(command.getCmdCode()); // 写入指令编号 |
| | | command.encode(out); // 编码具体负载 |
| | | // 打印 |
| | | if(command.getCmdCode()!=0x02) { |
| | | printHex(out); |
| | | } |
| | | } |
| | | private void printHex(ByteBuf out) |
| | | { |
| | | // 打印编码后的字节数组 |
| | | byte[] encodedBytes = new byte[out.readableBytes()]; // 创建字节数组,大小为可读字节数 |
| | | out.getBytes(out.readerIndex(), encodedBytes); // 读取ByteBuf中的字节到字节数组中 |
| | | // log.warn("Encoded message bytes: {}", Arrays.toString(encodedBytes)); |
| | | // 将字节数组转换为十六进制格式 |
| | | StringBuilder hexString = new StringBuilder(); |
| | | for (byte b : encodedBytes) { |
| | | hexString.append(String.format("%02X ", b)); // 将每个字节转换为两位十六进制并加上空格 |
| | | } |
| | | |
| | | log.warn("Encoded message in hex: {}", hexString.toString().trim()); |
| | | } |
| | | } |
| | |
| | | void registerMessage(); |
| | | |
| | | /** |
| | | * 无人机控制订阅 |
| | | */ |
| | | void uavControlSubscribe(String boxSn); |
| | | |
| | | /** |
| | | * 云台转动(基于角度) |
| | | */ |
| | | void controlAngle(String boxSn,Integer actionId,Integer rotationAngle) ; |
| | |
| | | this.sendMessage(tyConfiguration.getIpPort(), registerMessage); |
| | | } |
| | | |
| | | /** |
| | | * 无人机控制订阅 |
| | | */ |
| | | public void uavControlSubscribe(String boxSn) { |
| | | SubscribeCommand subscribeCommand = new SubscribeCommand(boxSn); |
| | | this.sendMessage(tyConfiguration.getIpPort(), subscribeCommand); |
| | | } |
| | | |
| | | /** |
| | | * 发送消息 |
| | |
| | | InetSocketAddress ipSocket = (InetSocketAddress) channel.remoteAddress(); |
| | | String host = ipSocket.getHostString(); |
| | | int port = ipSocket.getPort(); |
| | | // 创建 ByteBuf 并编码消息 |
| | | ByteBuf buffer = Unpooled.buffer(); |
| | | msg.encode(buffer); // 确保对消息进行编码 |
| | | channel.writeAndFlush(buffer); // 发送编码后的数据 |
| | | channel.writeAndFlush(msg); // 发送编码后的数据 |
| | | log.debug("向天宇服务器【{}:{}】发送数据,命令码:{}", host, port, String.format("0x%02X", msg.getCmdCode() & 0xFF)); |
| | | } else { |
| | | log.error("与天宇服务器链接断开,无法发送数据!"); |
| | |
| | | */ |
| | | @Override |
| | | public void controlAngle(String boxSn, Integer actionId, Integer rotationAngle) { |
| | | ControlAngleCommand controlAngleCommand = new ControlAngleCommand(boxSn.getBytes(), (byte) 0x00, actionId.byteValue(), rotationAngle.byteValue()); |
| | | ControlAngleCommand controlAngleCommand = new ControlAngleCommand(boxSn, actionId.byteValue(), rotationAngle.byteValue()); |
| | | this.sendMessage(tyConfiguration.getIpPort(), controlAngleCommand); |
| | | } |
| | | |
| | |
| | | break; |
| | | case 0x03: |
| | | // 处理订阅消息 |
| | | SubscribeCommand subscribeCommand = (SubscribeCommand) tyCommand; |
| | | log.info("收到订阅消息-->处理订阅消息{}", subscribeCommand.getBoxSn()); |
| | | break; |
| | | case 0x04: |
| | | // 处理取消订阅消息 |
| | | subscribeCommand = (SubscribeCommand) tyCommand; |
| | | log.info("收到取消订阅消息-->处理取消订阅消息{}", subscribeCommand.getBoxSn()); |
| | | break; |
| | | case (byte) 0xA8: |
| | | // 处理无人机状态消息 |
| | |
| | | // 处理无人机 |
| | | DroneReadyNotificationCommand droneReadyNotificationCommand = (DroneReadyNotificationCommand) tyCommand; |
| | | if (droneReadyNotificationCommand.getActionCode() == (byte) 0x42) { |
| | | UavInitInfoCommand uavInitCommand = new UavInitInfoCommand((byte) 0x00, (byte) 0x42, (byte) 0x01); |
| | | UavInitInfoCommand uavInitCommand = new UavInitInfoCommand(new String(droneReadyNotificationCommand.getBoxSn()), (byte) 0x42); |
| | | this.sendMessage(tyConfiguration.getIpPort(), uavInitCommand); |
| | | log.info("收到无人机初始化详情-回复防止再次发送"); |
| | | } |