修复地图yaml解析
This commit is contained in:
114
src/utils/rosMapYaml.ts
Normal file
114
src/utils/rosMapYaml.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
export interface ParsedRosMapYaml {
|
||||||
|
image?: string;
|
||||||
|
resolution: number | null;
|
||||||
|
origin: [number, number, number] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripInlineComment(value: string) {
|
||||||
|
let quote: string | null = null;
|
||||||
|
|
||||||
|
for (let index = 0; index < value.length; index += 1) {
|
||||||
|
const char = value[index];
|
||||||
|
const previousChar = value[index - 1];
|
||||||
|
|
||||||
|
if ((char === "'" || char === "\"") && previousChar !== "\\") {
|
||||||
|
quote = quote === char ? null : quote || char;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === "#" && !quote) {
|
||||||
|
return value.slice(0, index).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function unquoteYamlString(value: string) {
|
||||||
|
const trimmedValue = stripInlineComment(value);
|
||||||
|
|
||||||
|
return trimmedValue.replace(/^['"]|['"]$/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function readScalarValue(content: string, key: string) {
|
||||||
|
const match = content.match(new RegExp(`^\\s*${key}\\s*:\\s*(.*?)\\s*$`, "m"));
|
||||||
|
|
||||||
|
return match ? stripInlineComment(match[1]) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFiniteNumber(value: string) {
|
||||||
|
const parsedValue = Number(stripInlineComment(value));
|
||||||
|
|
||||||
|
return Number.isFinite(parsedValue) ? parsedValue : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseInlineOrigin(content: string) {
|
||||||
|
const originValue = content.match(/^\s*origin\s*:\s*\[([^\]]+)\]\s*(?:#.*)?$/m)?.[1];
|
||||||
|
|
||||||
|
if (!originValue) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = originValue
|
||||||
|
.split(",")
|
||||||
|
.map((value) => parseFiniteNumber(value.trim()))
|
||||||
|
.slice(0, 3);
|
||||||
|
|
||||||
|
return values.length === 3 && values.every((value) => value !== null)
|
||||||
|
? values as [number, number, number]
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseBlockOrigin(content: string) {
|
||||||
|
const lines = content.split(/\r?\n/);
|
||||||
|
const values: number[] = [];
|
||||||
|
|
||||||
|
for (let index = 0; index < lines.length; index += 1) {
|
||||||
|
const originLine = lines[index].match(/^(\s*)origin\s*:\s*(?:#.*)?$/);
|
||||||
|
|
||||||
|
if (!originLine) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originIndent = originLine[1].length;
|
||||||
|
|
||||||
|
for (let nextIndex = index + 1; nextIndex < lines.length; nextIndex += 1) {
|
||||||
|
const line = lines[nextIndex];
|
||||||
|
|
||||||
|
if (!line.trim() || /^\s*#/.test(line)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineIndent = line.match(/^\s*/)?.[0].length ?? 0;
|
||||||
|
if (lineIndent <= originIndent) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemValue = line.match(/^\s*-\s*(.*?)\s*$/)?.[1];
|
||||||
|
const parsedValue = itemValue ? parseFiniteNumber(itemValue) : null;
|
||||||
|
|
||||||
|
if (parsedValue === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
values.push(parsedValue);
|
||||||
|
|
||||||
|
if (values.length === 3) {
|
||||||
|
return values as [number, number, number];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseRosMapYaml(content: string): ParsedRosMapYaml {
|
||||||
|
const imageValue = readScalarValue(content, "image");
|
||||||
|
const resolution = parseFiniteNumber(readScalarValue(content, "resolution"));
|
||||||
|
const origin = parseInlineOrigin(content) || parseBlockOrigin(content);
|
||||||
|
|
||||||
|
return {
|
||||||
|
image: imageValue ? unquoteYamlString(imageValue) : undefined,
|
||||||
|
resolution: resolution && resolution > 0 ? resolution : null,
|
||||||
|
origin,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
updateRosCarMapUrls,
|
updateRosCarMapUrls,
|
||||||
uploadFile,
|
uploadFile,
|
||||||
} from "@/api/rosCar";
|
} from "@/api/rosCar";
|
||||||
|
import { parseRosMapYaml } from "@/utils/rosMapYaml";
|
||||||
|
|
||||||
type DeviceStatus = "空闲" | "巡检" | "离线" | "充电中";
|
type DeviceStatus = "空闲" | "巡检" | "离线" | "充电中";
|
||||||
type PatrolPointType = "业务点" | "途经点";
|
type PatrolPointType = "业务点" | "途经点";
|
||||||
@@ -436,27 +437,6 @@ function loadRosMapPreview(previewUrl: string, imageName: string) {
|
|||||||
scheduleMapCanvasRender();
|
scheduleMapCanvasRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析 ROS YAML 配置中的 image、resolution 和 origin 字段
|
|
||||||
function parseRosMapYaml(content: string) {
|
|
||||||
const image = content.match(/^\s*image\s*:\s*(.+?)\s*$/m)?.[1]?.replace(/^['"]|['"]$/g, "");
|
|
||||||
const resolution = Number(content.match(/^\s*resolution\s*:\s*([^\s#]+)\s*$/m)?.[1]);
|
|
||||||
const originValue = content.match(/^\s*origin\s*:\s*\[([^\]]+)\]\s*$/m)?.[1];
|
|
||||||
const origin = originValue
|
|
||||||
?.split(",")
|
|
||||||
.map((value) => Number(value.trim()))
|
|
||||||
.slice(0, 3);
|
|
||||||
|
|
||||||
return {
|
|
||||||
image,
|
|
||||||
resolution: Number.isFinite(resolution) && resolution > 0
|
|
||||||
? resolution
|
|
||||||
: null,
|
|
||||||
origin: origin?.length === 3 && origin.every((value) => Number.isFinite(value))
|
|
||||||
? origin as [number, number, number]
|
|
||||||
: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function readFileAsArrayBuffer(file: File) {
|
function readFileAsArrayBuffer(file: File) {
|
||||||
return new Promise<ArrayBuffer>((resolve, reject) => {
|
return new Promise<ArrayBuffer>((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
stopDispatchRosInspectionTask,
|
stopDispatchRosInspectionTask,
|
||||||
} from "@/api/rosCar";
|
} from "@/api/rosCar";
|
||||||
import { getToken } from "@/utils/auth";
|
import { getToken } from "@/utils/auth";
|
||||||
|
import { parseRosMapYaml } from "@/utils/rosMapYaml";
|
||||||
import { BASE_URL } from "@/utils/request";
|
import { BASE_URL } from "@/utils/request";
|
||||||
import { getRecentAlertPhoto } from "../shared/alertPhotos";
|
import { getRecentAlertPhoto } from "../shared/alertPhotos";
|
||||||
|
|
||||||
@@ -376,23 +377,6 @@ function buildFileAccessUrl(fileUrl: string) {
|
|||||||
return `${BASE_URL}${fileUrl.startsWith("/") ? fileUrl : `/${fileUrl}`}`;
|
return `${BASE_URL}${fileUrl.startsWith("/") ? fileUrl : `/${fileUrl}`}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析 ROS 地图 YAML 中的分辨率和原点,供真实坐标映射使用
|
|
||||||
function parseRosMapYaml(content: string): RosMapMeta {
|
|
||||||
const resolution = Number(content.match(/^\s*resolution\s*:\s*([^\s#]+)\s*$/m)?.[1]);
|
|
||||||
const originValue = content.match(/^\s*origin\s*:\s*\[([^\]]+)\]\s*$/m)?.[1];
|
|
||||||
const origin = originValue
|
|
||||||
?.split(",")
|
|
||||||
.map((value) => Number(value.trim()))
|
|
||||||
.slice(0, 3);
|
|
||||||
|
|
||||||
return {
|
|
||||||
resolution: Number.isFinite(resolution) && resolution > 0 ? resolution : 0.05,
|
|
||||||
origin: origin?.length === 3 && origin.every((value) => Number.isFinite(value))
|
|
||||||
? origin as [number, number, number]
|
|
||||||
: [0, 0, 0],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function readPgmToken(bytes: Uint8Array, cursor: { index: number }) {
|
function readPgmToken(bytes: Uint8Array, cursor: { index: number }) {
|
||||||
while (cursor.index < bytes.length) {
|
while (cursor.index < bytes.length) {
|
||||||
const code = bytes[cursor.index];
|
const code = bytes[cursor.index];
|
||||||
@@ -737,7 +721,11 @@ async function loadRobotMap(device: DeviceItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styledMap = buildStyledPgmMap(await pgmResponse.arrayBuffer());
|
const styledMap = buildStyledPgmMap(await pgmResponse.arrayBuffer());
|
||||||
const meta = parseRosMapYaml(await yamlResponse.text());
|
const parsedMeta = parseRosMapYaml(await yamlResponse.text());
|
||||||
|
const meta: RosMapMeta = {
|
||||||
|
resolution: parsedMeta.resolution ?? 0.05,
|
||||||
|
origin: parsedMeta.origin ?? [0, 0, 0],
|
||||||
|
};
|
||||||
|
|
||||||
rosMap.value = {
|
rosMap.value = {
|
||||||
...meta,
|
...meta,
|
||||||
|
|||||||
Reference in New Issue
Block a user