[add] 新增无人机模块后端项目

[refactor] 重构后端项目
This commit is contained in:
lcj
2025-05-21 11:30:59 +08:00
parent 4c238435d8
commit dc1de34116
1699 changed files with 54361 additions and 325 deletions

View File

@ -0,0 +1,297 @@
package com.ruoyi.wayline.waypoint;
/**
* @Author zhouzhixiong
* @Date 2023/12/2 17:04
*/
import com.ruoyi.wayline.domain.Action;
import com.ruoyi.wayline.domain.Placemark;
import com.ruoyi.wayline.domain.WaypointData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
public class WayPointKmlProcessorModify {
/**
* 根据提供的数据请求处理并生成航点飞行的 KML 文件
*
* @param dataRequest
* @return
*/
public byte[] processKml(WaypointData dataRequest, int templateId) {
try {
// 创建文件的编辑对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 从资源中读取 KML 模板文件
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("waypoint/template.kml");
Document document = builder.parse(inputStream);
// 使用提供的数据请求更新文档
updateDocumentWithRequestData(document, dataRequest, templateId);
// 将更新后的文档转换为字节数组
return convertDocumentToByteArray(document);
} catch (ParserConfigurationException | SAXException | IOException | TransformerException e) {
throw new RuntimeException(e);
}
}
/**
* 根据请求中的信息来更新文档内容
*
* @param document
* @param dataRequest
*/
private void updateDocumentWithRequestData(Document document, WaypointData dataRequest, int templateId) {
// 更新安全起飞高度
document.getElementsByTagName("wpml:takeOffSecurityHeight").item(0).setTextContent(String.valueOf(dataRequest.getTakeOffSecurityHeight()));
// 更新参考起飞点
document.getElementsByTagName("wpml:takeOffRefPoint").item(0).setTextContent(dataRequest.getTakeOffRefPoint());
// 更新飞向首航点速度
document.getElementsByTagName("wpml:globalTransitionalSpeed").item(0).setTextContent(String.valueOf(dataRequest.getGlobalTransitionalSpeed()));
// 更新无人机的信息和负载的信息
updateDroneAndPayloadInfo(document, dataRequest);
// 生成并设置 templateId
document.getElementsByTagName("wpml:templateId").item(0).setTextContent(String.valueOf(templateId));
// 更新全局航线速度
document.getElementsByTagName("wpml:autoFlightSpeed").item(0).setTextContent(String.valueOf(dataRequest.getAutoFlightSpeed()));
// 更新相对起飞点高度
document.getElementsByTagName("wpml:globalHeight").item(0).setTextContent(String.valueOf(dataRequest.getGlobalHeight()));
// 更新飞行器离被摄面高度(相对地面高)
document.getElementsByTagName("wpml:globalShootHeight").item(0).setTextContent(String.valueOf(dataRequest.getGlobalShootHeight()));
// 更新图片格式列表
document.getElementsByTagName("wpml:imageFormat").item(0).setTextContent(dataRequest.getImageFormat());
// 删除模板中现有的所有 <Placemark> 节点
removeExistingPlacemarks(document);
// TODO 根据提供的位置列表添加若干新的 <Placemark> 节点
Element folderElement = (Element) document.getElementsByTagName("Folder").item(0);
for (int index = 0; index < dataRequest.getPlacemarkList().size(); index++) {
Placemark location = dataRequest.getPlacemarkList().get(index);
folderElement.appendChild(createPlacemarkElement(document, index, location, dataRequest));
}
// 更新文档中全部的负载位置索引标签
updateAllPayloadPositionIndexes(document, dataRequest);
}
/**
* 更新所有 <wpml:payloadPositionIndex> 标签
*
* @param document
* @param dataRequest
*/
private void updateAllPayloadPositionIndexes(Document document, WaypointData dataRequest) {
NodeList payloadPositionIndexes = document.getElementsByTagName("wpml:payloadPositionIndex");
for (int i = 0; i < payloadPositionIndexes.getLength(); i++) {
payloadPositionIndexes.item(i).setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadPositionIndex()));
}
}
/**
* 创建一个新的 <Placemark> 元素
*
* @param document
* @param index
* @param location
* @return
*/
private Element createPlacemarkElement(Document document, int index, Placemark location, WaypointData dataRequest) {
// 创建 <Placemark> 根节点
Element placemark = document.createElement("Placemark");
// 设置 <Point> 元素及其坐标
Element point = document.createElement("Point");
Element coordinatesElement = document.createElement("coordinates");
String coordinates = location.getLongitude() + "," + location.getLatitude();
coordinatesElement.setTextContent(coordinates);
point.appendChild(coordinatesElement);
placemark.appendChild(point);
// 设置 <wpml:index> 元素
placemark.appendChild(createElementWithTextContent(document, "wpml:index", String.valueOf(index)));
// 设置其他 <Placemark> 元素的属性
placemark.appendChild(createElementWithTextContent(document, "wpml:ellipsoidHeight", String.valueOf(location.getAltitude())));
placemark.appendChild(createElementWithTextContent(document, "wpml:height", String.valueOf(location.getAltitude())));
placemark.appendChild(createElementWithTextContent(document, "wpml:useGlobalHeight", "0"));
placemark.appendChild(createElementWithTextContent(document, "wpml:useGlobalSpeed", "1"));
placemark.appendChild(createElementWithTextContent(document, "wpml:useGlobalHeadingParam", "1"));
placemark.appendChild(createElementWithTextContent(document, "wpml:useGlobalTurnParam", "1"));
placemark.appendChild(createElementWithTextContent(document, "wpml:useStraightLine", "1"));
placemark.appendChild(createElementWithTextContent(document, "wpml:gimbalPitchAngle", "0.0"));
placemark.appendChild(createElementWithTextContent(document, "wpml:waypointSpeed", String.valueOf(dataRequest.getAutoFlightSpeed())));
// 根据每个 Placemark 的动作列表创建动作组
createActionGroup(document, location, placemark, index);
return placemark;
}
/**
* 更新无人机的信息和负载信息
*
* @param document
* @param dataRequest
*/
public void updateDroneAndPayloadInfo(Document document, WaypointData dataRequest) {
// 更新飞行器的信息
NodeList droneInfoNodes = document.getElementsByTagName("wpml:droneInfo").item(0).getChildNodes();
for (int i = 0; i < droneInfoNodes.getLength(); i++) {
Node node = droneInfoNodes.item(i);
if (node.getNodeName().equals("wpml:droneEnumValue")) {
node.setTextContent(String.valueOf(dataRequest.getDroneInfo().getDroneEnumValue()));
} else if (node.getNodeName().equals("wpml:droneSubEnumValue")) {
node.setTextContent(String.valueOf(dataRequest.getDroneInfo().getDroneSubEnumValue()));
}
}
// 更新负载的信息
NodeList payloadInfoNodes = document.getElementsByTagName("wpml:payloadInfo").item(0).getChildNodes();
for (int i = 0; i < payloadInfoNodes.getLength(); i++) {
Node node = payloadInfoNodes.item(i);
if (node.getNodeName().equals("wpml:payloadEnumValue")) {
node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadEnumValue()));
} else if (node.getNodeName().equals("wpml:payloadSubEnumValue")) {
node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadSubEnumValue()));
} else if (node.getNodeName().equals("wpml:payloadPositionIndex")) {
node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadPositionIndex()));
}
}
}
/**
* 从文档中移除所有现有的 <Placemark> 节点
*
* @param document
*/
public void removeExistingPlacemarks(Document document) {
NodeList placemarks = document.getElementsByTagName("Placemark");
int length = placemarks.getLength();
for (int i = length - 1; i >= 0; i--) {
Node placemark = placemarks.item(i);
placemark.getParentNode().removeChild(placemark);
}
}
/**
* 创建带文本内容的新元素
*
* @param document
* @param tagName
* @param textContent
* @return
*/
public Element createElementWithTextContent(Document document, String tagName, String textContent) {
Element element = document.createElement(tagName);
element.setTextContent(textContent);
return element;
}
/**
* 创建动作组
*
* @param document
* @param placemark
*/
private void createActionGroup(Document document, Placemark placemark, Element placemarkElement, int index) {
// 创建 <wpml:actionGroup> 节点
Element actionGroup = document.createElement("wpml:actionGroup");
actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupId", "0"));
actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupStartIndex", String.valueOf(index)));
actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupEndIndex", String.valueOf(index)));
actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupMode", "sequence"));
Element actionTrigger = document.createElement("wpml:actionTrigger");
actionTrigger.appendChild(createElementWithTextContent(document, "wpml:actionTriggerType", "reachPoint"));
actionGroup.appendChild(actionTrigger);
// 添加每个动作到动作组中
List<Action> actions = placemark.getActions();
if (actions == null || actions.isEmpty()) {
return;
}
for (int i = 0; i < actions.size(); i++) {
Action action = actions.get(i);
Element actionElement = createActionElement(document, i, action.getType(), action.getParams());
actionGroup.appendChild(actionElement);
}
// 将动作组添加到 Placemark
placemarkElement.appendChild(actionGroup);
}
private Element createActionElement(Document document, int actionId, String actionActuatorFunc, Map<String, String> actionParams) {
Element actionElement = document.createElement("wpml:action");
// 设置动作ID
actionElement.appendChild(createElementWithTextContent(document, "wpml:actionId", String.valueOf(actionId)));
// 设置动作类型
actionElement.appendChild(createElementWithTextContent(document, "wpml:actionActuatorFunc", actionActuatorFunc));
// 创建并设置动作参数
Element actionParamsElement = document.createElement("wpml:actionActuatorFuncParam");
for (Map.Entry<String, String> param : actionParams.entrySet()) {
actionParamsElement.appendChild(createElementWithTextContent(document, "wpml:" + param.getKey(), param.getValue()));
}
actionElement.appendChild(actionParamsElement);
return actionElement;
}
/**
* 将文档转换为字节数组
*
* @param document 要转换的文档
* @return 转换后的字节数组
* @throws TransformerException
*/
public byte[] convertDocumentToByteArray(Document document) throws TransformerException {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
DOMSource source = new DOMSource(document);
StringWriter stringWriter = new StringWriter();
StreamResult result = new StreamResult(stringWriter);
transformer.transform(source, result);
String xmlString = stringWriter.toString();
// 使用正则表达式移除空白行
xmlString = xmlString.replaceAll("(?m)^[ \t]*\r?\n", "");
return xmlString.getBytes(StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,210 @@
package com.ruoyi.wayline.waypoint;
import com.ruoyi.wayline.domain.Placemark;
import com.ruoyi.wayline.domain.WaypointData;
import com.ruoyi.wayline.domain.Action;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
public class WayPointWpmlProcessorModify {
public byte[] processWpml(WaypointData dataRequest, int templateId) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("waypoint/waylines.wpml");
Document document = builder.parse(inputStream);
updateDocumentWithRequest(document, dataRequest, templateId);
return convertDocumentToByteArray(document);
} catch (ParserConfigurationException | SAXException | IOException | TransformerException e) {
throw new RuntimeException(e);
}
}
private void updateDocumentWithRequest(Document document, WaypointData dataRequest, int templateId) {
document.getElementsByTagName("wpml:takeOffSecurityHeight").item(0).setTextContent(String.valueOf(dataRequest.getTakeOffSecurityHeight()));
document.getElementsByTagName("wpml:globalTransitionalSpeed").item(0).setTextContent(String.valueOf(dataRequest.getGlobalTransitionalSpeed()));
updateDroneAndPayloadInfo(document, dataRequest);
document.getElementsByTagName("wpml:templateId").item(0).setTextContent(String.valueOf(templateId));
document.getElementsByTagName("wpml:autoFlightSpeed").item(0).setTextContent(String.valueOf(dataRequest.getAutoFlightSpeed()));
removeExistingPlacemarks(document);
Element folderElement = (Element) document.getElementsByTagName("Folder").item(0);
List<Placemark> placemarkList = dataRequest.getPlacemarkList();
for (int index = 0; index < placemarkList.size(); index++) {
Placemark location = placemarkList.get(index);
String coordinates = location.getLongitude() + "," + location.getLatitude();
String height = String.valueOf(dataRequest.getGlobalHeight());
folderElement.appendChild(createPlacemarkElement(document, coordinates, location, dataRequest, index, height));
}
updateAllPayloadPositionIndexes(document, dataRequest);
}
private void updateAllPayloadPositionIndexes(Document document, WaypointData dataRequest) {
NodeList payloadPositionIndexes = document.getElementsByTagName("wpml:payloadPositionIndex");
for (int i = 0; i < payloadPositionIndexes.getLength(); i++) {
payloadPositionIndexes.item(i).setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadPositionIndex()));
}
}
private Element createPlacemarkElement(Document document, String coordinates, Placemark placemarkData, WaypointData dataRequest, int index, String height) {
Element placemark = document.createElement("Placemark");
Element point = document.createElement("Point");
Element coordinatesElement = document.createElement("coordinates");
coordinatesElement.setTextContent(coordinates);
point.appendChild(coordinatesElement);
placemark.appendChild(point);
placemark.appendChild(createElementWithTextContent(document, "wpml:index", String.valueOf(index)));
placemark.appendChild(createElementWithTextContent(document, "wpml:executeHeight", placemarkData.getAltitude()));
placemark.appendChild(createElementWithTextContent(document, "wpml:waypointSpeed", String.valueOf(dataRequest.getAutoFlightSpeed())));
addWaypointHeadingParam(placemark, document);
addWaypointTurnParam(placemark, document);
addWaypointGimbalHeadingParam(placemark, document);
placemark.appendChild(createElementWithTextContent(document, "wpml:useStraightLine", "1"));
createActionGroup(document, placemark, placemarkData, index);
return placemark;
}
private void createActionGroup(Document document, Element placemarkElement, Placemark placemarkData, int index) {
Element actionGroup = document.createElement("wpml:actionGroup");
actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupId", "0"));
actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupStartIndex", String.valueOf(index)));
actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupEndIndex", String.valueOf(index)));
actionGroup.appendChild(createElementWithTextContent(document, "wpml:actionGroupMode", "sequence"));
Element actionTrigger = document.createElement("wpml:actionTrigger");
actionTrigger.appendChild(createElementWithTextContent(document, "wpml:actionTriggerType", "reachPoint"));
actionGroup.appendChild(actionTrigger);
// 动态添加动作
List<Action> actions = placemarkData.getActions();
if (actions == null || actions.isEmpty()) {
return;
}
for (int i = 0; i < actions.size(); i++) {
Action action = actions.get(i);
Element actionElement = createActionElement(document, i, action.getType(), action.getParams());
actionGroup.appendChild(actionElement);
}
placemarkElement.appendChild(actionGroup);
}
private Element createActionElement(Document document, int actionId, String actionActuatorFunc, Map<String, String> actionParams) {
Element actionElement = document.createElement("wpml:action");
actionElement.appendChild(createElementWithTextContent(document, "wpml:actionId", String.valueOf(actionId)));
actionElement.appendChild(createElementWithTextContent(document, "wpml:actionActuatorFunc", actionActuatorFunc));
Element actionParamsElement = document.createElement("wpml:actionActuatorFuncParam");
for (Map.Entry<String, String> param : actionParams.entrySet()) {
actionParamsElement.appendChild(createElementWithTextContent(document, "wpml:" + param.getKey(), param.getValue()));
}
actionElement.appendChild(actionParamsElement);
return actionElement;
}
private void addWaypointHeadingParam(Element placemark, Document document) {
Element waypointHeadingParam = document.createElement("wpml:waypointHeadingParam");
waypointHeadingParam.appendChild(createElementWithTextContent(document, "wpml:waypointHeadingMode", "followWayline"));
waypointHeadingParam.appendChild(createElementWithTextContent(document, "wpml:waypointHeadingAngle", "0"));
waypointHeadingParam.appendChild(createElementWithTextContent(document, "wpml:waypointHeadingPathMode", "followBadArc"));
placemark.appendChild(waypointHeadingParam);
}
private void addWaypointTurnParam(Element placemark, Document document) {
Element waypointTurnParam = document.createElement("wpml:waypointTurnParam");
waypointTurnParam.appendChild(createElementWithTextContent(document, "wpml:waypointTurnMode", "toPointAndStopWithDiscontinuityCurvature"));
waypointTurnParam.appendChild(createElementWithTextContent(document, "wpml:waypointTurnDampingDist", "0.0"));
placemark.appendChild(waypointTurnParam);
}
private void addWaypointGimbalHeadingParam(Element placemark, Document document) {
Element waypointGimbalHeadingParam = document.createElement("wpml:waypointGimbalHeadingParam");
waypointGimbalHeadingParam.appendChild(createElementWithTextContent(document, "wpml:waypointGimbalPitchAngle", "0.0"));
placemark.appendChild(waypointGimbalHeadingParam);
}
public void updateDroneAndPayloadInfo(Document document, WaypointData dataRequest) {
NodeList droneInfoNodes = document.getElementsByTagName("wpml:droneInfo").item(0).getChildNodes();
for (int i = 0; i < droneInfoNodes.getLength(); i++) {
Node node = droneInfoNodes.item(i);
if (node.getNodeName().equals("wpml:droneEnumValue")) {
node.setTextContent(String.valueOf(dataRequest.getDroneInfo().getDroneEnumValue()));
} else if (node.getNodeName().equals("wpml:droneSubEnumValue")) {
node.setTextContent(String.valueOf(dataRequest.getDroneInfo().getDroneSubEnumValue()));
}
}
NodeList payloadInfoNodes = document.getElementsByTagName("wpml:payloadInfo").item(0).getChildNodes();
for (int i = 0; i < payloadInfoNodes.getLength(); i++) {
Node node = payloadInfoNodes.item(i);
if (node.getNodeName().equals("wpml:payloadEnumValue")) {
node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadEnumValue()));
} else if (node.getNodeName().equals("wpml:payloadSubEnumValue")) {
node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadSubEnumValue()));
} else if (node.getNodeName().equals("wpml:payloadPositionIndex")) {
node.setTextContent(String.valueOf(dataRequest.getPayloadInfo().getPayloadPositionIndex()));
}
}
}
public void removeExistingPlacemarks(Document document) {
NodeList placemarks = document.getElementsByTagName("Placemark");
int length = placemarks.getLength();
for (int i = length - 1; i >= 0; i--) {
Node placemark = placemarks.item(i);
placemark.getParentNode().removeChild(placemark);
}
}
public Element createElementWithTextContent(Document document, String tagName, String textContent) {
Element element = document.createElement(tagName);
element.setTextContent(textContent);
return element;
}
public byte[] convertDocumentToByteArray(Document document) throws TransformerException {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
DOMSource source = new DOMSource(document);
StringWriter stringWriter = new StringWriter();
StreamResult result = new StreamResult(stringWriter);
transformer.transform(source, result);
String xmlString = stringWriter.toString();
xmlString = xmlString.replaceAll("(?m)^[ \t]*\r?\n", "");
return xmlString.getBytes(StandardCharsets.UTF_8);
}
}