| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- import {
- fetchRemoteJson,
- isRemoteImageUrl,
- resolveResourceUrl,
- } from "./wrwRemoteData";
- import { formatWakeMetric, formatWakePercent } from "./wrwTableUtils";
- /** 接口 *Path 字段 → 前端图表数据 key */
- const CHART_PATH_MAP = [
- { chartKey: "windSpeedDistribution", pathKey: "windSpeedDistributionChart" },
- { chartKey: "wakeRose", pathKey: "wakeRoseDiagramPath" },
- { chartKey: "wakeDistribute", pathKey: "wakeDistributePath" },
- { chartKey: "wakeLossConfident", pathKey: "wakeLossConfidentPath" },
- { chartKey: "velocityDeficitBar", pathKey: "velocityDeficitPath" },
- ];
- const CHART_PAYLOAD_ALIASES = {
- windSpeedDistribution: ["windSpeedDistribution", "windSpeedDistributionChart"],
- wakeRose: ["wakeRose", "windRose"],
- wakeDistribute: ["wakeDistribute", "layoutLossScatter"],
- wakeLossConfident: ["wakeLossConfident", "lossConfidenceScatter"],
- velocityDeficitBar: ["velocityDeficitBar", "velocityDeficit", "lossDelBar"],
- };
- function emptyPageData() {
- return {
- charts: {},
- tableData: [],
- turbineNames: [],
- wakeDescription: "",
- meta: {},
- };
- }
- function unwrapWindRosePayload(payload) {
- if (!payload || typeof payload !== "object") return null;
- let current = payload;
- for (let depth = 0; depth < 4; depth += 1) {
- if (current.windRose && typeof current.windRose === "object") {
- current = current.windRose;
- continue;
- }
- if (current.wakeRose && typeof current.wakeRose === "object") {
- current = current.wakeRose;
- continue;
- }
- if (current.data && typeof current.data === "object") {
- const nested =
- current.data.windRose || current.data.wakeRose || current.data;
- if (nested && nested !== current) {
- current = nested;
- continue;
- }
- }
- break;
- }
- return current;
- }
- function normalizeChartPayload(chartKey, payload) {
- if (!payload || typeof payload !== "object") return null;
- if (chartKey === "wakeRose") {
- return unwrapWindRosePayload(payload);
- }
- const aliases = CHART_PAYLOAD_ALIASES[chartKey] || [chartKey];
- for (let i = 0; i < aliases.length; i += 1) {
- if (payload[aliases[i]]) {
- return payload[aliases[i]];
- }
- }
- if (Array.isArray(payload.turbines) || Array.isArray(payload.points)) {
- return payload;
- }
- if (Array.isArray(payload.items)) {
- return payload;
- }
- if (Array.isArray(payload.bands)) {
- return payload;
- }
- return payload;
- }
- function hasChartPayload(chartKey, data) {
- if (!data || typeof data !== "object") return false;
- if (data.__imageUrl) return true;
- if (Array.isArray(data.points) && data.points.length) return true;
- if (Array.isArray(data.items) && data.items.length) return true;
- if (Array.isArray(data.turbines) && data.turbines.length) return true;
- if (chartKey === "wakeRose" && !isEmptyWakeRose(data)) return true;
- return false;
- }
- function normalizeChartsObject(rawCharts = {}, vo = {}) {
- const charts = { ...(rawCharts || {}) };
- Object.keys(CHART_PAYLOAD_ALIASES).forEach((chartKey) => {
- if (hasChartPayload(chartKey, charts[chartKey])) return;
- const aliases = CHART_PAYLOAD_ALIASES[chartKey] || [chartKey];
- for (let i = 0; i < aliases.length; i += 1) {
- const alias = aliases[i];
- const sources = [charts[alias], vo[alias]];
- for (let j = 0; j < sources.length; j += 1) {
- const source = sources[j];
- if (!source) continue;
- const normalized = normalizeChartPayload(chartKey, source);
- if (hasChartPayload(chartKey, normalized)) {
- charts[chartKey] = normalized;
- return;
- }
- }
- }
- });
- return charts;
- }
- async function loadChartFromPath(chartKey, path) {
- if (!path) return null;
- const resolved = resolveResourceUrl(path);
- if (isRemoteImageUrl(resolved)) {
- return chartKey === "wakeDistribute" ? { __imageUrl: resolved } : null;
- }
- try {
- const json = await fetchRemoteJson(resolved);
- return normalizeChartPayload(chartKey, json);
- } catch (error) {
- console.warn(`加载尾流图表资源失败 [${chartKey}]:`, path, error);
- return null;
- }
- }
- async function loadDescriptionText(path) {
- if (!path) return "";
- const resolved = resolveResourceUrl(path);
- if (isRemoteImageUrl(resolved)) return "";
- try {
- const response = await fetch(resolved, { method: "GET", credentials: "include" });
- if (!response.ok) return "";
- return response.text();
- } catch (error) {
- console.warn("加载尾流描述文本失败:", path, error);
- return "";
- }
- }
- function formatConfidence(confidentLevel) {
- return formatWakePercent(confidentLevel);
- }
- function formatNumber(value) {
- return formatWakeMetric(value);
- }
- export function mapWakeTurbineList(list) {
- if (!Array.isArray(list)) return [];
- return list.map((item) => ({
- name: item.engineId || item.engineName || item.name || "—",
- engineId: item.engineId || "",
- affected: item.isWake === 1 || item.isWake === true ? "yes" : "no",
- turbulence: formatNumber(item.turbulenceIntensity),
- loss: formatNumber(item.velocityDeficit),
- confidence: formatConfidence(item.confidentLevel),
- image: resolveResourceUrl(item.wakeTurbinePath || item.image || ""),
- }));
- }
- function extractTurbineNames(charts, turbineList) {
- const names = [];
- const pushName = (id) => {
- if (id && !names.includes(id)) names.push(id);
- };
- charts.windSpeedDistribution?.turbines?.forEach((item) => {
- pushName(item.displayName || item.engineId);
- });
- charts.wakeRose?.turbines?.forEach((item) => {
- pushName(item.engineId);
- });
- if (Array.isArray(turbineList)) {
- turbineList.forEach((item) => pushName(item.engineId));
- }
- return names;
- }
- function formatDataDate(dataDate) {
- if (!dataDate) return "";
- return String(dataDate).slice(0, 10);
- }
- function isEmptyWakeRose(rose) {
- return !rose || !Array.isArray(rose.turbines) || !rose.turbines.length;
- }
- function hasRoseBands(turbine) {
- return (
- turbine &&
- Array.isArray(turbine.bands) &&
- turbine.bands.some((band) => Array.isArray(band.values) && band.values.length)
- );
- }
- function normalizeTurbineRoseData(engineId, payload) {
- const normalized = unwrapWindRosePayload(payload);
- if (!normalized || typeof normalized !== "object") return null;
- const meta = {
- speedBands: normalized.speedBands,
- directions: normalized.directions,
- };
- if (Array.isArray(normalized.turbines) && normalized.turbines.length) {
- const matched =
- normalized.turbines.find((item) => item.engineId === engineId) ||
- normalized.turbines[0];
- if (!hasRoseBands(matched)) return null;
- return {
- engineId: matched.engineId || engineId,
- bands: matched.bands,
- speedBands: meta.speedBands,
- directions: meta.directions,
- };
- }
- if (hasRoseBands(normalized)) {
- return {
- engineId: normalized.engineId || engineId,
- bands: normalized.bands,
- speedBands: meta.speedBands,
- directions: meta.directions,
- };
- }
- return null;
- }
- function getTurbineRosePath(item) {
- const path =
- item?.wakeRoseDiagramPath ||
- item?.windRoseDiagramPath ||
- item?.wakeRosePath ||
- "";
- return typeof path === "string" ? path.trim() : "";
- }
- function getInlineTurbineRoseSource(item) {
- if (!item || typeof item !== "object") return null;
- const pathField =
- item.wakeRoseDiagramPath || item.windRoseDiagramPath || item.wakeRosePath;
- if (pathField && typeof pathField === "object") {
- return normalizeTurbineRoseData(item.engineId, pathField);
- }
- if (item.windRose || item.wakeRose) {
- return normalizeTurbineRoseData(
- item.engineId,
- item.windRose || item.wakeRose,
- );
- }
- return null;
- }
- async function loadTurbineRoseFromItem(item) {
- const engineId = item?.engineId || "";
- const inlineRose = getInlineTurbineRoseSource(item);
- if (inlineRose) return inlineRose;
- const rosePath = getTurbineRosePath(item);
- if (!rosePath || isRemoteImageUrl(resolveResourceUrl(rosePath))) {
- return null;
- }
- try {
- const json = await fetchRemoteJson(rosePath);
- return normalizeTurbineRoseData(engineId, json);
- } catch (error) {
- console.warn(`加载机组玫瑰图失败 [${engineId}]:`, rosePath, error);
- return null;
- }
- }
- async function loadTurbineRoseDiagrams(turbineList) {
- if (!Array.isArray(turbineList) || !turbineList.length) {
- return [];
- }
- const results = await Promise.all(
- turbineList.map((item) => loadTurbineRoseFromItem(item)),
- );
- return results.filter(Boolean);
- }
- function mergeWakeRoseData(farmRose, turbineList, perTurbineRoses) {
- const roseMap = new Map();
- (farmRose?.turbines || []).forEach((item) => {
- if (item?.engineId) roseMap.set(item.engineId, item);
- });
- perTurbineRoses.forEach((item) => {
- if (item?.engineId) roseMap.set(item.engineId, item);
- });
- if (!roseMap.size) return farmRose || null;
- const turbines = [];
- if (Array.isArray(turbineList)) {
- turbineList.forEach((item) => {
- const id = item?.engineId;
- if (id && roseMap.has(id)) {
- turbines.push(roseMap.get(id));
- roseMap.delete(id);
- }
- });
- }
- roseMap.forEach((item) => turbines.push(item));
- const metaSource =
- perTurbineRoses.find((item) => item.speedBands || item.directions) ||
- farmRose ||
- {};
- return {
- speedBands: metaSource.speedBands || farmRose?.speedBands,
- directions: metaSource.directions || farmRose?.directions,
- turbines: turbines.map(({ speedBands, directions, ...item }) => item),
- };
- }
- export async function loadWakePageData(vo) {
- if (!vo || typeof vo !== "object") {
- return emptyPageData();
- }
- const turbineList = Array.isArray(vo.getWakeTurbineList)
- ? vo.getWakeTurbineList
- : [];
- let charts = normalizeChartsObject(
- vo.charts && typeof vo.charts === "object" ? vo.charts : {},
- vo,
- );
- const [wakeDescription, perTurbineRoses] = await Promise.all([
- loadDescriptionText(vo.wakeDiscriptionPath),
- loadTurbineRoseDiagrams(turbineList),
- ...CHART_PATH_MAP.filter(({ chartKey }) => chartKey !== "wakeRose").map(
- async ({ chartKey, pathKey }) => {
- if (charts[chartKey]) return;
- const loaded = await loadChartFromPath(chartKey, vo[pathKey]);
- if (loaded) {
- charts[chartKey] = loaded;
- }
- },
- ),
- ]);
- const mergedWakeRose = mergeWakeRoseData(null, turbineList, perTurbineRoses);
- if (mergedWakeRose?.turbines?.length) {
- charts.wakeRose = mergedWakeRose;
- } else if (isEmptyWakeRose(charts.wakeRose)) {
- const farmRose = await loadChartFromPath("wakeRose", vo.wakeRoseDiagramPath);
- if (farmRose && !isEmptyWakeRose(farmRose)) {
- charts.wakeRose = farmRose;
- }
- }
- const tableData = mapWakeTurbineList(turbineList);
- const turbineNames = extractTurbineNames(charts, turbineList);
- if (
- !hasChartPayload("velocityDeficitBar", charts.velocityDeficitBar) &&
- turbineList.length
- ) {
- charts.velocityDeficitBar = {
- items: turbineList.map((item) => ({
- engineId: item.engineId,
- lossPercent: item.lossPercent ?? item.velocityDeficit,
- turbulenceIntensity: item.turbulenceIntensity,
- velocityDeficit: item.velocityDeficit,
- })),
- };
- }
- if (
- !hasChartPayload("wakeLossConfident", charts.wakeLossConfident) &&
- turbineList.length
- ) {
- charts.wakeLossConfident = {
- points: turbineList.map((item) => ({
- engineId: item.engineId,
- velocityDeficit: item.velocityDeficit,
- confidentLevel: item.confidentLevel,
- })),
- };
- }
- return {
- charts,
- tableData,
- turbineNames,
- wakeDescription,
- turbineList,
- meta: {
- fieldId: vo.fieldId || "",
- dataDate: formatDataDate(vo.dataDate),
- },
- };
- }
|