ard-work/src/main/java/com/ard/tianyu/controller/UavWayLineController.java
New file @@ -0,0 +1,39 @@ package com.ard.tianyu.controller; import com.ard.common.core.domain.AjaxResult; import com.ard.tianyu.model.dto.speaker.SpeakModeSwitchDTO; import com.ard.tianyu.model.dto.speaker.StopSpeakDTO; import com.ard.tianyu.model.dto.speaker.TextSpeakDTO; import com.ard.tianyu.model.dto.wayline.WayLineUploadDTO; import com.ard.tianyu.service.ITyService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * UavSpeakerController * * @author liusuyi * @date 2024年10月21日13:52 **/ @Api(tags = "无人机航线飞行接口") @RequestMapping("/uav/wayline") @RestController public class UavWayLineController { @Resource private ITyService tyService; @ApiOperation("上传航线") @PostMapping("/wayLineUpload") public AjaxResult wayLineUpload(@RequestBody WayLineUploadDTO wayLineUploadDTO) { tyService.wayLineUpload(wayLineUploadDTO); return AjaxResult.success(); } } ard-work/src/main/java/com/ard/tianyu/model/cache/TyTcpServerCache.java
New file @@ -0,0 +1,31 @@ package com.ard.tianyu.model.cache; import com.ard.tianyu.model.dto.signal.SignalInfo; import java.util.concurrent.ConcurrentHashMap; /** * 天宇tcp服务器缓存 * * @author liusuyi * @date 2024年11月01日13:53 **/ public class TyTcpServerCache { // 使用 ConcurrentHashMap 来缓存TCP服务连接状态,key 是 ip:port(服务器地址) value 是连接状态 private static final ConcurrentHashMap<String, Boolean> tcpServerCache = new ConcurrentHashMap<>(); // 存储TCP服务连接状态到缓存 public static void putTcpServerCache(String tcpServer,Boolean state) { tcpServerCache.put(tcpServer, state); } // 从缓存中获取TCP服务连接状态 public static Boolean getTcpServerCache(String tcpServer) { return tcpServerCache.getOrDefault(tcpServer,false); } // 删除缓存中的TCP服务连接状态 public static void removeTcpServerCache(String tcpServer) { tcpServerCache.remove(tcpServer); } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/wayline/HangarPointConverter.java
New file @@ -0,0 +1,29 @@ package com.ard.tianyu.model.dto.converter.wayline; import com.ard.common.utils.bean.BeanUtils; import com.ard.tianyu.model.dto.wayline.HangarPoint; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; public class HangarPointConverter { public static TelemetryDataPB.HangarPoint convertEntityToProto(HangarPoint hangarPoint) { // 使用 Protobuf 的构建器创建 HangarPoint 对象 TelemetryDataPB.HangarPoint.Builder builder = TelemetryDataPB.HangarPoint.newBuilder(); // 从 HangarPoint 实体中获取值并设置到 Protobuf 对象 builder.setHangarId(hangarPoint.getHangarId()); builder.setHangarLng(hangarPoint.getHangarLng()); builder.setHangarLat(hangarPoint.getHangarLat()); builder.setAlternateLng(hangarPoint.getAlternateLng()); builder.setAlternateLat(hangarPoint.getAlternateLat()); // 构建并返回 Protobuf 对象 return builder.build(); } // 将 Protobuf 对象转换为实体对象 public static HangarPoint convertProtoToEntity(TelemetryDataPB.HangarPoint proto) { HangarPoint hangarPoint = new HangarPoint(); // 从 Protobuf 对象中获取值并设置到实体对象 BeanUtils.copyProperties(proto, hangarPoint); return hangarPoint; } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/wayline/NormalModelConverter.java
New file @@ -0,0 +1,48 @@ package com.ard.tianyu.model.dto.converter.wayline; import com.ard.common.utils.bean.BeanUtils; import com.ard.tianyu.model.dto.wayline.NormalModel; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; public class NormalModelConverter { public static TelemetryDataPB.NormalModel convertEntityToProto(NormalModel normalModel) { // 使用构建器来创建 Protobuf 对象 TelemetryDataPB.NormalModel.Builder builder = TelemetryDataPB.NormalModel.newBuilder(); // 复制基本属性 builder.setServiceId(normalModel.getServiceId()); builder.setCameraId(normalModel.getCameraId()); builder.setIsModeling(normalModel.getIsModeling()); builder.setMode(normalModel.getMode()); builder.setHOverlap(normalModel.getHOverlap()); builder.setVOverlap(normalModel.getVOverlap()); builder.setMirror(normalModel.getMirror()); // 处理 repeated fields if (normalModel.getArea() != null) { normalModel.getArea().forEach(pointData -> { builder.addArea(PointDataConverter.convertEntityToProto(pointData)); }); } // 构建并返回 Protobuf 对象 return builder.build(); } // 将 Protobuf 对象转换为实体对象 public static NormalModel convertProtoToEntity(TelemetryDataPB.NormalModel proto) { NormalModel normalModel = new NormalModel(); // 复制基本属性 BeanUtils.copyProperties(proto, normalModel); // 处理 repeated fields if (proto.getAreaCount() > 0) { proto.getAreaList().forEach(pointDataProto -> { normalModel.getArea().add(PointDataConverter.convertProtoToEntity(pointDataProto)); }); } return normalModel; } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/wayline/PlanLineDataConverter.java
New file @@ -0,0 +1,128 @@ package com.ard.tianyu.model.dto.converter.wayline; import com.ard.common.utils.bean.BeanUtils; import com.ard.tianyu.model.dto.state.PointData; import com.ard.tianyu.model.dto.wayline.HangarPoint; import com.ard.tianyu.model.dto.wayline.NormalModel; import com.ard.tianyu.model.dto.wayline.PlanLineData; import com.ard.tianyu.model.dto.wayline.SurroundModel; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import java.util.List; import java.util.stream.Collectors; public class PlanLineDataConverter { // 将 Protobuf 对象转换为实体对象 public static PlanLineData convertProtoToEntity(TelemetryDataPB.PlanLineData proto) { PlanLineData planLineData = new PlanLineData(); // 使用 BeanUtils 复制属性 BeanUtils.copyProperties(proto, planLineData); // 处理 repeated fields if (proto.getPointsCount() > 0) { List<PointData> points = proto.getPointsList().stream() .map(PointDataConverter::convertProtoToEntity) .collect(Collectors.toList()); planLineData.setPoints(points); } if (proto.getInterestsCount() > 0) { List<PointData> interests = proto.getInterestsList().stream() .map(PointDataConverter::convertProtoToEntity) .collect(Collectors.toList()); planLineData.setInterests(interests); } // 处理 HangarPoint if (proto.hasTakePoint()) { HangarPoint takePoint = HangarPointConverter.convertProtoToEntity(proto.getTakePoint()); planLineData.setTakePoint(takePoint); } if (proto.hasLandPoint()) { HangarPoint landPoint = HangarPointConverter.convertProtoToEntity(proto.getLandPoint()); planLineData.setLandPoint(landPoint); } // 处理 NormalModel 和 SurroundModel if (proto.hasNormalModel()) { NormalModel normalModel = NormalModelConverter.convertProtoToEntity(proto.getNormalModel()); planLineData.setNormalModel(normalModel); } if (proto.hasSurroundModel()) { SurroundModel surroundModel = SurroundModelConverter.convertProtoToEntity(proto.getSurroundModel()); planLineData.setSurroundModel(surroundModel); } // 处理起飞点 if (proto.hasTakeOffPoint()) { PointData takeOffPoint = PointDataConverter.convertProtoToEntity(proto.getTakeOffPoint()); planLineData.setTakeOffPoint(takeOffPoint); } return planLineData; } // 将实体对象转换为 Protobuf 对象 public static TelemetryDataPB.PlanLineData convertEntityToProto(PlanLineData planLineData) { // 创建 Protobuf 的构建器 TelemetryDataPB.PlanLineData.Builder builder = TelemetryDataPB.PlanLineData.newBuilder(); // 复制基本属性 builder.setFinishedAction(planLineData.getFinishedAction()); builder.setMaxSpeed(planLineData.getMaxSpeed()); builder.setAutoSpeed(planLineData.getAutoSpeed()); builder.setLoseAction(planLineData.getLoseAction()); builder.setHomeHeight(planLineData.getHomeHeight()); builder.setIsSaveEnergyMode(planLineData.getIsSaveEnergyMode()); // 处理 repeated fields if (planLineData.getPoints() != null) { List<TelemetryDataPB.PointData> points = planLineData.getPoints().stream() .map(PointDataConverter::convertEntityToProto) .collect(Collectors.toList()); builder.addAllPoints(points); } if (planLineData.getInterests() != null) { List<TelemetryDataPB.PointData> interests = planLineData.getInterests().stream() .map(PointDataConverter::convertEntityToProto) .collect(Collectors.toList()); builder.addAllInterests(interests); } // 处理 HangarPoint if (planLineData.getTakePoint() != null) { TelemetryDataPB.HangarPoint takePoint = HangarPointConverter.convertEntityToProto(planLineData.getTakePoint()); builder.setTakePoint(takePoint); } if (planLineData.getLandPoint() != null) { TelemetryDataPB.HangarPoint landPoint = HangarPointConverter.convertEntityToProto(planLineData.getLandPoint()); builder.setLandPoint(landPoint); } // 处理 NormalModel 和 SurroundModel if (planLineData.getNormalModel() != null) { TelemetryDataPB.NormalModel normalModel = NormalModelConverter.convertEntityToProto(planLineData.getNormalModel()); builder.setNormalModel(normalModel); } if (planLineData.getSurroundModel() != null) { TelemetryDataPB.SurroundModel surroundModel = SurroundModelConverter.convertEntityToProto(planLineData.getSurroundModel()); builder.setSurroundModel(surroundModel); } // 处理起飞点 if (planLineData.getTakeOffPoint() != null) { TelemetryDataPB.PointData takeOffPoint = PointDataConverter.convertEntityToProto(planLineData.getTakeOffPoint()); builder.setTakeOffPoint(takeOffPoint); } // 构建并返回 Protobuf 对象 return builder.build(); } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/wayline/PointActionConverter.java
New file @@ -0,0 +1,27 @@ package com.ard.tianyu.model.dto.converter.wayline; import com.ard.common.utils.bean.BeanUtils; import com.ard.tianyu.model.dto.wayline.PointAction; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; public class PointActionConverter { public static TelemetryDataPB.PointAction convertEntityToProto(PointAction pointAction) { // 使用构建器来创建 Protobuf 对象 TelemetryDataPB.PointAction.Builder builder = TelemetryDataPB.PointAction.newBuilder(); // 复制基本属性 builder.setType(pointAction.getType()); builder.setParam(pointAction.getParam()); builder.setWaitTime(pointAction.getWaitTime()); builder.setSpeakInfo(pointAction.getSpeakInfo()); // 构建并返回 Protobuf 对象 return builder.build(); } public static PointAction convertProtoToEntity(TelemetryDataPB.PointAction proto) { PointAction pointAction = new PointAction(); BeanUtils.copyProperties(proto, pointAction); return pointAction; } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/wayline/PointDataConverter.java
New file @@ -0,0 +1,53 @@ package com.ard.tianyu.model.dto.converter.wayline; import com.ard.common.utils.bean.BeanUtils; import com.ard.tianyu.model.dto.state.PointData; import com.ard.tianyu.model.dto.wayline.PointAction; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import java.util.List; import java.util.stream.Collectors; public class PointDataConverter { public static TelemetryDataPB.PointData convertEntityToProto(PointData pointData) { // 使用构建器来创建 Protobuf 对象 TelemetryDataPB.PointData.Builder builder = TelemetryDataPB.PointData.newBuilder(); // 复制基本属性 builder.setLng(pointData.getLng()); builder.setLat(pointData.getLat()); builder.setHeight(pointData.getHeight()); builder.setSpeed(pointData.getSpeed()); builder.setFlightPathMode(pointData.getFlightPathMode()); builder.setDampingDistance(pointData.getDampingDistance()); builder.setSafeGohomeFlag(pointData.getSafeGohomeFlag()); builder.setHeadingMode(pointData.getHeadingMode()); builder.setHeading(pointData.getHeading()); builder.setInterestIndex(pointData.getInterestIndex()); builder.setGroupIndex(pointData.getGroupIndex()); // 处理动作数组 if (pointData.getActions() != null && !pointData.getActions().isEmpty()) { List<TelemetryDataPB.PointAction> actions = pointData.getActions().stream() .map(PointActionConverter::convertEntityToProto) .collect(Collectors.toList()); builder.addAllActions(actions); } // 构建并返回 Protobuf 对象 return builder.build(); } public static PointData convertProtoToEntity(TelemetryDataPB.PointData proto) { PointData pointData = new PointData(); BeanUtils.copyProperties(proto, pointData); // 处理 repeated fields if (proto.getActionsCount() > 0) { List<PointAction> actions = proto.getActionsList().stream() .map(PointActionConverter::convertProtoToEntity) .collect(Collectors.toList()); pointData.setActions(actions); } return pointData; } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/wayline/SurroundModelConverter.java
New file @@ -0,0 +1,47 @@ package com.ard.tianyu.model.dto.converter.wayline; import com.ard.common.utils.bean.BeanUtils; import com.ard.tianyu.model.dto.state.PointData; import com.ard.tianyu.model.dto.wayline.SurroundModel; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import java.util.List; import java.util.stream.Collectors; public class SurroundModelConverter { public static TelemetryDataPB.SurroundModel convertEntityToProto(SurroundModel surroundModel) { // 使用构建器来创建 Protobuf 对象 TelemetryDataPB.SurroundModel.Builder builder = TelemetryDataPB.SurroundModel.newBuilder(); // 复制基本属性 builder.setServiceId(surroundModel.getServiceId()); builder.setCameraId(surroundModel.getCameraId()); builder.setIsModeling(surroundModel.getIsModeling()); // 处理 repeated fields if (surroundModel.getArea() != null && !surroundModel.getArea().isEmpty()) { List<TelemetryDataPB.PointData> area = surroundModel.getArea().stream() .map(PointDataConverter::convertEntityToProto) .collect(Collectors.toList()); builder.addAllArea(area); } // 构建并返回 Protobuf 对象 return builder.build(); } public static SurroundModel convertProtoToEntity(TelemetryDataPB.SurroundModel proto) { SurroundModel surroundModel = new SurroundModel(); BeanUtils.copyProperties(proto, surroundModel); // 处理 repeated fields if (proto.getAreaCount() > 0) { List<PointData> area = proto.getAreaList().stream() .map(PointDataConverter::convertProtoToEntity) .collect(Collectors.toList()); surroundModel.setArea(area); } return surroundModel; } } ard-work/src/main/java/com/ard/tianyu/model/dto/state/PointData.java
@@ -1,10 +1,24 @@ package com.ard.tianyu.model.dto.state; import com.ard.tianyu.model.dto.wayline.PointAction; import lombok.Data; import java.util.List; // 假设 PointData 类定义如下 @Data public class PointData { private double lat; // 纬度 private double lng; // 经度 private float height; // 高度(单位:米,范围0~遥控器限高) private float speed; // 速度(单位:米/秒,范围0~15),默认8 private List<PointAction> actions; // 动作数组 private int flightPathMode; // 航线飞行模式:1=直线飞行,2=协调转弯,3=曲线飞行(停),4=曲线飞行(不停) private float dampingDistance; // 转弯半径(仅协调转弯模式下有效),取值范围1~655.35单位:米 private int safeGohomeFlag; // 是否为安全返航点 0=不是,1=是 private int headingMode; // 偏航模式 0=自动,1=锁定,2=遥控器控制,3=云台依照航点方向旋转,4=朝向兴趣点,5=飞机和云台的偏航同时旋转 private float heading; // 航点偏航 -180~180 private int interestIndex; // 兴趣点下标,默认-1 private int groupIndex; // 组下标 } ard-work/src/main/java/com/ard/tianyu/model/dto/wayline/HangarPoint.java
New file @@ -0,0 +1,23 @@ package com.ard.tianyu.model.dto.wayline; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * 机库信息实体对象 * * @author liusuyi * @date 2024年11月01日10:55 **/ @Data @Builder @AllArgsConstructor @NoArgsConstructor public class HangarPoint { private String hangarId; // 机库ID private double hangarLng; // 机库经度 private double hangarLat; // 机库纬度 private double alternateLng; // 备降点经度 private double alternateLat; // 备降点纬度 } ard-work/src/main/java/com/ard/tianyu/model/dto/wayline/NormalModel.java
New file @@ -0,0 +1,30 @@ package com.ard.tianyu.model.dto.wayline; import com.ard.tianyu.model.dto.state.PointData; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; /** * 普通建模实体对象 * * @author liusuyi * @date 2024年11月01日10:55 **/ @Data @Builder @AllArgsConstructor @NoArgsConstructor public class NormalModel { private List<PointData> area; // 作业区域 private int serviceId; // 建模平台编号:1=瞰景,2=大势,默认1 private int cameraId; // 相机编号 private int isModeling; // 是否实时建模:0=不,1=是 private int mode; // 作业模式:1=倾斜,2=正摄 private int hOverlap; // 横向重叠率 private int vOverlap; // 纵向重叠率 private int mirror; // 方向正反 } ard-work/src/main/java/com/ard/tianyu/model/dto/wayline/PlanLineData.java
New file @@ -0,0 +1,34 @@ package com.ard.tianyu.model.dto.wayline; import com.ard.tianyu.model.dto.state.PointData; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; /** * 航线实体对象 * * @author liusuyi * @date 2024年11月01日10:55 **/ @Data @Builder @AllArgsConstructor @NoArgsConstructor public class PlanLineData { private int finishedAction; // 结束航线后动作 private float maxSpeed; // 最大飞行速度(单位:米/秒) private float autoSpeed; // 自动飞行速度(单位:米/秒) private List<PointData> points; // 坐标点数组 private int loseAction; // 网络失联后动作 private int homeHeight; // 返航高度 private int isSaveEnergyMode; // 是否开启节能模式 private HangarPoint takePoint; // 起飞点机库信息 private HangarPoint landPoint; // 降落点机库信息 private List<PointData> interests; // 兴趣点数组 private NormalModel normalModel; // 普通建模 private SurroundModel surroundModel; // 环绕建模 private PointData takeOffPoint; // 起飞点 } ard-work/src/main/java/com/ard/tianyu/model/dto/wayline/PointAction.java
New file @@ -0,0 +1,22 @@ package com.ard.tianyu.model.dto.wayline; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * 航点动作实体对象 * * @author liusuyi * @date 2024年11月01日10:55 **/ @Data @Builder @AllArgsConstructor @NoArgsConstructor public class PointAction { private int type; // 类型:1-变焦 2-拍照 3-录像 4-停录 5-控制飞机机头偏航 6-控制云台俯仰 7-控制云台偏航 8-控制云台横滚 9-悬停 10-等距间隔拍照 11-等时间隔拍照 12-结束间隔拍照 13-单条喊话 14-开始循环喊话 15-结束循环喊话 16-对焦 private float param; // 参数,根据类型不同含义不同 private int waitTime; // 动作触发后等待时长(单位:秒) private String speakInfo; // 喊话内容[type=13和type=14时生效] } ard-work/src/main/java/com/ard/tianyu/model/dto/wayline/SurroundModel.java
New file @@ -0,0 +1,14 @@ package com.ard.tianyu.model.dto.wayline; import com.ard.tianyu.model.dto.state.PointData; import lombok.Data; import java.util.List; @Data public class SurroundModel { private List<PointData> area; // 作业区域 private int serviceId; // 建模平台编号:1=瞰景 private int cameraId; // 相机编号 private int isModeling; // 是否实时建模:0=不,1=是 } ard-work/src/main/java/com/ard/tianyu/model/dto/wayline/WayLineUploadDTO.java
New file @@ -0,0 +1,22 @@ package com.ard.tianyu.model.dto.wayline; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * 上传航线实体对象 * * @author liusuyi * @date 2024年11月01日10:55 **/ @Data @Builder @AllArgsConstructor @NoArgsConstructor public class WayLineUploadDTO { private String boxSn; private PlanLineData wayLineData; } ard-work/src/main/java/com/ard/tianyu/netty/TyNettyTcpClient.java
@@ -1,6 +1,7 @@ package com.ard.tianyu.netty; import com.ard.tianyu.config.TyConfiguration; import com.ard.tianyu.model.cache.TyTcpServerCache; import com.ard.tianyu.netty.handler.ClientHandler; import com.ard.tianyu.netty.handler.MessageProtocolDecoder; import com.ard.tianyu.netty.handler.MessageProtocolEncoder; @@ -72,10 +73,12 @@ ChannelFuture future = bootstrap.connect(host, port).addListener((ChannelFuture futureListener) -> { if (futureListener.isSuccess()) { log.info("连接天宇TCP服务器成功【{}:{}】", host, port); TyTcpServerCache.putTcpServerCache(host + ":" + port, true); } else { final EventLoop eventLoop = futureListener.channel().eventLoop(); eventLoop.schedule(() -> { log.info("连接天宇TCP服务器失败【{}:{}】,正尝试重连...", host, port); TyTcpServerCache.putTcpServerCache(host + ":" + port, false); try { initConnection(host, port); } catch (Exception e) { ard-work/src/main/java/com/ard/tianyu/netty/command/wayline/WayLineUploadCmd.java
New file @@ -0,0 +1,40 @@ package com.ard.tianyu.netty.command.wayline; import com.ard.tianyu.model.dto.converter.wayline.PlanLineDataConverter; import com.ard.tianyu.model.dto.wayline.PlanLineData; import com.ard.tianyu.model.dto.wayline.WayLineUploadDTO; import com.ard.tianyu.netty.command.BaseCmd; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import io.netty.buffer.ByteBuf; /** * 上传航线指令 * * @author liusuyi * @date 2024年10月30日16:20 **/ public class WayLineUploadCmd extends BaseCmd { private final byte[] wayLineData; //动作参数 public WayLineUploadCmd(WayLineUploadDTO wayLineUploadDTO) { PlanLineData planLineData = wayLineUploadDTO.getWayLineData(); TelemetryDataPB.PlanLineData planLineDataPB = PlanLineDataConverter.convertEntityToProto(planLineData); this.wayLineData = planLineDataPB.toByteArray(); setDataHeader((short) 0x7479); setDataLength((short) (18 + wayLineData.length)); setBoxSn(wayLineUploadDTO.getBoxSn().getBytes()); setCmdCode((byte) 0xD1); } public void encode(ByteBuf out) { out.writeShort(getDataHeader()); // 写入数据头 out.writeShort(getDataLength()); // 写入数据长度 out.writeBytes(getBoxSn()); // 写入盒子SN out.writeByte(getCmdCode()); // 写入命令码 out.writeByte(0x00); // 写入加密标志 out.writeByte(0x10); // 写入动作编号 out.writeBytes(this.wayLineData); // 写入航线数据 } } ard-work/src/main/java/com/ard/tianyu/netty/command/wayline/WayLineUploadReplyCmd.java
New file @@ -0,0 +1,37 @@ package com.ard.tianyu.netty.command.wayline; import com.ard.tianyu.model.dto.converter.wayline.PlanLineDataConverter; import com.ard.tianyu.model.dto.wayline.PlanLineData; import com.ard.tianyu.model.dto.wayline.WayLineUploadDTO; import com.ard.tianyu.netty.command.BaseCmd; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import io.netty.buffer.ByteBuf; import lombok.Data; /** * 上传航线回复指令 * * @author liusuyi * @date 2024年10月30日16:20 **/ @Data public class WayLineUploadReplyCmd extends BaseCmd { private final byte encryptFlag = 0x00; // 加密标志 private final byte actionCode = 0x10; // 动作编号 private final byte result; // 执行结果 private final Integer errorCode; // 错误码 public WayLineUploadReplyCmd(String boxSn, Byte result, Integer errorCode) { setDataHeader((short) 0x6A77); //setDataLength((short) 18); setCmdCode((byte) 0xD1); setBoxSn(boxSn.getBytes()); this.result = result; this.errorCode = errorCode; } public void encode(ByteBuf out) { } } ard-work/src/main/java/com/ard/tianyu/netty/handler/ClientHandler.java
@@ -1,5 +1,6 @@ package com.ard.tianyu.netty.handler; import com.ard.tianyu.model.cache.TyTcpServerCache; import com.ard.tianyu.netty.TyNettyTcpClient; import com.ard.tianyu.netty.command.BaseCmd; import com.ard.tianyu.service.ITyService; @@ -70,7 +71,7 @@ int port = ipSocket.getPort(); String host = ipSocket.getHostString(); log.error("与天宇服务{}:{}连接断开!", host, port); TyTcpServerCache.putTcpServerCache(host + ":" + port, false); ctx.channel().eventLoop().schedule(() -> { log.error("天宇服务端链接不上,开始重连操作..."); try { ard-work/src/main/java/com/ard/tianyu/netty/handler/MessageProtocolDecoder.java
@@ -16,6 +16,7 @@ import com.ard.tianyu.netty.command.register.HeartbeatReplyCmd; import com.ard.tianyu.netty.command.register.RegisterReplyCmd; import com.ard.tianyu.netty.command.register.UavInitReplyCmd; import com.ard.tianyu.netty.command.wayline.WayLineUploadReplyCmd; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import com.google.protobuf.InvalidProtocolBufferException; import io.netty.buffer.ByteBuf; @@ -113,6 +114,8 @@ in.readBytes(uavData); String boxSn = new String(uavData); switch (actionCode) { case 0x10: return new WayLineUploadReplyCmd(boxSn, result, errorCode); case 0x11: return new TakeOffReplyCmd(boxSn, result, errorCode); case 0x12: ard-work/src/main/java/com/ard/tianyu/service/ITyService.java
@@ -12,6 +12,7 @@ import com.ard.tianyu.model.dto.speaker.*; import com.ard.tianyu.model.dto.state.UavState; import com.ard.tianyu.model.dto.telemetry.TelemetryData; import com.ard.tianyu.model.dto.wayline.WayLineUploadDTO; import com.ard.tianyu.model.receiver.*; import com.ard.tianyu.netty.command.BaseCmd; import com.dtflys.forest.annotation.Var; @@ -229,6 +230,11 @@ void stopSpeak(StopSpeakDTO stopSpeakDTO); /** * 上传航线 */ void wayLineUpload(WayLineUploadDTO wayLineUploadDTO); /** * 处理回复消息 */ void processReplyMessage(String key, BaseCmd tyMessage); ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java
@@ -3,6 +3,7 @@ import com.ard.common.utils.ByteUtils; import com.ard.tianyu.config.TyConfiguration; import com.ard.tianyu.http.TyClient; import com.ard.tianyu.model.cache.TyTcpServerCache; import com.ard.tianyu.model.cache.UavSignalInfoCache; import com.ard.tianyu.model.cache.UavStateCache; import com.ard.tianyu.model.cache.UavTelemetryDataCache; @@ -16,11 +17,14 @@ import com.ard.tianyu.model.dto.converter.UavSignalInfoConverter; import com.ard.tianyu.model.dto.converter.UavStateConverter; import com.ard.tianyu.model.dto.converter.UavTelemetryDataConverter; import com.ard.tianyu.model.dto.converter.wayline.PlanLineDataConverter; import com.ard.tianyu.model.dto.manual.*; import com.ard.tianyu.model.dto.signal.SignalInfo; import com.ard.tianyu.model.dto.speaker.*; import com.ard.tianyu.model.dto.state.UavState; import com.ard.tianyu.model.dto.telemetry.TelemetryData; import com.ard.tianyu.model.dto.wayline.PlanLineData; import com.ard.tianyu.model.dto.wayline.WayLineUploadDTO; import com.ard.tianyu.model.receiver.*; import com.ard.tianyu.netty.command.*; import com.ard.tianyu.netty.TyNettyTcpClient; @@ -37,7 +41,10 @@ import com.ard.tianyu.netty.command.real.UavTelemetryDataCmd; import com.ard.tianyu.netty.command.register.*; import com.ard.tianyu.netty.command.speaker.*; import com.ard.tianyu.netty.command.wayline.WayLineUploadCmd; import com.ard.tianyu.netty.command.wayline.WayLineUploadReplyCmd; import com.ard.tianyu.netty.handler.ErrorCodeEnum; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import com.ard.tianyu.service.ITyService; import com.ard.tianyu.websocket.utils.WebSocketUtil; import io.netty.channel.Channel; @@ -49,6 +56,7 @@ import javax.annotation.Resource; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -420,6 +428,7 @@ RecordSpeakCmd recordSpeakCmd = new RecordSpeakCmd(recordSpeakDTO); this.sendMessage(tyConfiguration.getIpPort(), recordSpeakCmd); } /** * 设置音量 */ @@ -437,13 +446,58 @@ LoopSettingCmd loopSettingCmd = new LoopSettingCmd(loopSettingDTO); this.sendMessage(tyConfiguration.getIpPort(), loopSettingCmd); } /** * 停止喊话 */ @Override public void stopSpeak(StopSpeakDTO stopSpeakDTO) { StopSpeakCmd stopSpeakCmd=new StopSpeakCmd(stopSpeakDTO); StopSpeakCmd stopSpeakCmd = new StopSpeakCmd(stopSpeakDTO); this.sendMessage(tyConfiguration.getIpPort(), stopSpeakCmd); } /** * 航线上传 */ @Override public void wayLineUpload(WayLineUploadDTO wayLineUploadDTO) { wayLineUploadDTO.setWayLineData(this.CreatePlanLineData());//设置航线数据 WayLineUploadCmd wayLineUploadCmd = new WayLineUploadCmd(wayLineUploadDTO); this.sendMessage(tyConfiguration.getIpPort(), wayLineUploadCmd); } //创建航线 private PlanLineData CreatePlanLineData() { TelemetryDataPB.PointData pointData1 = TelemetryDataPB.PointData.newBuilder() .setLng(124.12345).setLat(45.12345).setHeight(80).setSpeed(10) .setFlightPathMode(1)//航线飞行模式:1=直线飞行,2=协调转弯,3=曲线飞行(停),4=曲线飞行(不停) .setSafeGohomeFlag(0)//是否为安全返航点 0=不是,1=是 .build(); TelemetryDataPB.PointData pointData2 = TelemetryDataPB.PointData.newBuilder() .setLng(124.22222).setLat(45.22222).setHeight(80).setSpeed(10) .setFlightPathMode(1)//航线飞行模式:1=直线飞行,2=协调转弯,3=曲线飞行(停),4=曲线飞行(不停) .setSafeGohomeFlag(0)//是否为安全返航点 0=不是,1=是 .build(); TelemetryDataPB.PointData pointData3 = TelemetryDataPB.PointData.newBuilder() .setLng(124.33333).setLat(45.33333).setHeight(80).setSpeed(10) .setFlightPathMode(1)//航线飞行模式:1=直线飞行,2=协调转弯,3=曲线飞行(停),4=曲线飞行(不停) .setSafeGohomeFlag(0)//是否为安全返航点 0=不是,1=是 .build(); //创建航线 TelemetryDataPB.PlanLineData PlanLineDataPB = TelemetryDataPB.PlanLineData.newBuilder() .setFinishedAction(2)//结束航线后动作:1=返航,2=原地悬停,3=原地降落,4=原地绕飞 .setMaxSpeed(10)//最大飞行速度 .setAutoSpeed(10)//自动飞行速度 .addPoints(pointData1)//航线点1 .addPoints(pointData2)//航线点2 .addPoints(pointData3)//航线点3 .setLoseAction(1)//网络失联后动作:0=返回HOME点,1=继续航线,默认0 .setHomeHeight(6)//返航高度:(单位:米,范围20~1500) .setIsSaveEnergyMode(0)////是否开启节能模式:0=不是,1=是(说明:节能模式下,飞机沿最短直线距离由起飞点到航线第一个航点,非升高到第一个航点高度再飞往第一个航点) .build(); return PlanLineDataConverter.convertProtoToEntity(PlanLineDataPB); } /** @@ -589,7 +643,12 @@ //紧急制动回复 if (tyCommand instanceof EmergencyBrakeReplyCmd) { EmergencyBrakeReplyCmd emergencyBrakeReplyCmd = (EmergencyBrakeReplyCmd) tyCommand; logPrint("紧急制动回复", emergencyBrakeReplyCmd.getResult(), emergencyBrakeReplyCmd.getErrorCode()); logPrint("紧急制动", emergencyBrakeReplyCmd.getResult(), emergencyBrakeReplyCmd.getErrorCode()); } //上传航线回复 if (tyCommand instanceof WayLineUploadReplyCmd) { WayLineUploadReplyCmd wayLineUploadReplyCmd = (WayLineUploadReplyCmd) tyCommand; logPrint("上传航线", wayLineUploadReplyCmd.getResult(), wayLineUploadReplyCmd.getErrorCode()); } break; default: @@ -617,6 +676,10 @@ */ @Scheduled(fixedRate = 3000) public void sendHeartBeat() { if(!TyTcpServerCache.getTcpServerCache(tyConfiguration.getTcpserver() + ":" + tyConfiguration.getTcpport())) { return; } long timestamp = System.currentTimeMillis(); log.debug("定时向天宇发送⼼跳,时间戳{}", timestamp); HeartbeatCmd heartbeatMessage = new HeartbeatCmd(timestamp); ard-work/src/main/java/com/ard/vtdu/service/impl/MediaServiceImpl.java
@@ -2,6 +2,8 @@ import com.ard.common.constant.HttpStatus; import com.ard.common.core.page.TableDataInfo; import com.ard.tianyu.config.TyConfiguration; import com.ard.tianyu.model.cache.TyTcpServerCache; import com.ard.tianyu.model.receiver.BoxInfoReceiver; import com.ard.tianyu.model.receiver.BoxStreamUrlReceiver; import com.ard.tianyu.service.ITyService; @@ -42,6 +44,8 @@ private MediaClient mediaClient; @Resource private ITyService tyService; @Resource private TyConfiguration tyConfiguration; @Value("${mediamtx.host}") private String mediamtxHost; @@ -566,8 +570,11 @@ } //同步无人机推流到流媒体 @Scheduled(cron = "0/20 * * * * ?") // @Scheduled(cron = "0/20 * * * * ?") public void syncVtdu() { if (!TyTcpServerCache.getTcpServerCache(tyConfiguration.getTcpserver() + ":" + tyConfiguration.getTcpport())) { return; } log.info("定时同步MediaMTX任务开始"); List<String> nameList = getNameList(); List<BoxInfoReceiver> boxList = tyService.getBoxList();