| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807 |
- /**
- * 异常检测图表视觉语义(前端全场图 + 报告共用)
- *
- * 原则:
- * - 颜色优先表达诊断结论,不用彩虹色表达大量风机编号
- * - 异常使用红色和更高层级,散点统一圆形,避免只依赖颜色
- * - 连续变量使用感知均匀、色盲友好的 Viridis 色带
- */
- export const DIAGNOSTIC_COLORS = {
- normal: "#2E90FA",
- anomaly: "#F04438",
- warning: "#F79009",
- reference: "#98A2B3",
- contract: "#E11D48",
- /** 全场风功率总图合同曲线:与异常红、正常蓝分开 */
- farmContract: "#7C3AED",
- mean: "#7F56D9",
- /** 单机降载点:深琥珀,浅底上要和正常蓝点分开 */
- deload: "#A16207",
- /** 单机风功率散点:停机 */
- shutdown: "#111827",
- blade1: "#2E90FA",
- blade2: "#12B76A",
- blade3: "#7F56D9",
- };
- export const DIAGNOSTIC_CATEGORICAL_COLORS = [
- "#2E90FA",
- "#12B76A",
- "#F79009",
- "#7F56D9",
- "#06AED4",
- "#EE46BC",
- "#6172F3",
- "#47CD89",
- "#FDB022",
- "#84ADFF",
- ];
- /** 全场总图:正常风机冷色,异常风机红橙暖色;同组内用不同色相区分机组 */
- export const FARM_NORMAL_COOL_COLORS = Object.freeze([
- "#3B82F6",
- "#06B6D4",
- "#0EA5E9",
- "#2563EB",
- "#14B8A6",
- "#6366F1",
- "#38BDF8",
- "#0D9488",
- ]);
- export const FARM_ANOMALY_WARM_COLORS = Object.freeze([
- "#F04438",
- "#F97316",
- "#EF4444",
- "#FB7185",
- "#EA580C",
- "#E11D48",
- "#F59E0B",
- "#DC2626",
- ]);
- export const DIAGNOSTIC_CONTINUOUS_COLORS = [
- "#440154",
- "#3B528B",
- "#21918C",
- "#5EC962",
- "#FDE725",
- ];
- /** 报告点数色带:白 → 浅蓝 → 深蓝(浅底报告) */
- export const DIAGNOSTIC_COUNT_COLORS = [
- "#FFFFFF",
- "#D6E8F8",
- "#7EB6E0",
- "#2E75B6",
- "#0B3A6E",
- ];
- /**
- * 点数色带:低点数深、高点数亮。页面使用。
- */
- export const PAGE_DIAGNOSTIC_COUNT_COLORS = [
- "#1a2740",
- "#234b73",
- "#2f74a8",
- "#3d9fd4",
- "#6ec8f0",
- ];
- /**
- * 单机风功率散点「停机」点色。页面科技蓝深底需浅色才可见,
- * 浅色主题沿用 DIAGNOSTIC_COLORS.shutdown;报告始终用 shutdown 深色。
- */
- export const PAGE_SHUTDOWN_COLORS = Object.freeze({
- dark: "#CBD5E1",
- light: DIAGNOSTIC_COLORS.shutdown,
- });
- /** 报告最小桨距角色带停靠:0% / 10% / 20% / 30% / 100% */
- export const PITCH_MIN_REPORT_COLOR_STOPS = [
- [0, "#e6f4f5"],
- [0.1, "#74ABF7"],
- [0.2, "#3E7AF6"],
- [0.3, "#0000E4"],
- [1, "#020085"],
- ];
- function hexToRgb(hex) {
- const text = String(hex || "").replace("#", "");
- return [
- parseInt(text.slice(0, 2), 16),
- parseInt(text.slice(2, 4), 16),
- parseInt(text.slice(4, 6), 16),
- ];
- }
- function rgbToHex(rgb) {
- return `#${rgb
- .map((value) =>
- Math.max(0, Math.min(255, Math.round(value)))
- .toString(16)
- .padStart(2, "0"),
- )
- .join("")}`;
- }
- /** 按停靠位置铺成等距色阶,供 ECharts 连续色带按比例取色 */
- export function expandColorStops(stops, steps = 101) {
- const sorted = [...stops].sort((a, b) => a[0] - b[0]);
- const colors = [];
- const last = Math.max(steps - 1, 1);
- for (let index = 0; index < steps; index += 1) {
- const t = index / last;
- let upper = sorted.findIndex((stop) => stop[0] >= t);
- if (upper < 0) upper = sorted.length - 1;
- const lower = Math.max(0, upper - (sorted[upper][0] === t ? 0 : 1));
- const [startT, startColor] = sorted[lower];
- const [endT, endColor] = sorted[upper];
- if (startT === endT) {
- colors.push(endColor);
- continue;
- }
- const ratio = (t - startT) / (endT - startT);
- const startRgb = hexToRgb(startColor);
- const endRgb = hexToRgb(endColor);
- colors.push(
- rgbToHex(
- startRgb.map(
- (value, channel) => value + (endRgb[channel] - value) * ratio,
- ),
- ),
- );
- }
- return colors;
- }
- export const PITCH_MIN_REPORT_COUNT_COLORS = expandColorStops(
- PITCH_MIN_REPORT_COLOR_STOPS,
- );
- export const DIAGNOSTIC_FARM_STYLES = Object.freeze({
- normal: Object.freeze({
- pointOpacity: 0.5,
- lineWidth: 1,
- lineOpacity: 0.5,
- z: 1,
- }),
- anomaly: Object.freeze({
- pointOpacity: 1,
- lineWidth: 2.4,
- lineOpacity: 1,
- z: 20,
- }),
- contract: Object.freeze({
- pointOpacity: 1,
- lineWidth: 3.8,
- lineOpacity: 1,
- z: 40,
- }),
- category: Object.freeze({
- pointOpacity: 0.88,
- lineWidth: 1.8,
- lineOpacity: 1,
- z: 2,
- }),
- });
- /** 全场图:正常点统一色、异常点统一红,图例按异常机组优先 */
- export const FARM_STATUS_LEGEND_DETECTORS = new Set([
- "wind_power_curve",
- "wind_power_scatter",
- "yaw_static",
- "yaw_twist",
- "pitch_coord",
- "pitch_regulation",
- "ctrl_power_quality",
- "ctrl_op_state",
- "ctrl_deload",
- "aero_cp",
- "aero_tsr",
- "aero_tsr_wind",
- ]);
- /** 按散点/点状态上色(正常点蓝、异常点红;以 pointAnomaly 或系列名拆分为准) */
- const FARM_POINT_STATUS_COLOR_DETECTORS = new Set([
- "wind_power_curve",
- "wind_power_scatter",
- "yaw_static",
- "yaw_twist",
- "pitch_coord",
- "aero_cp",
- "aero_tsr",
- "aero_tsr_wind",
- ]);
- export function usesFarmPointStatusColor(templateKey = "") {
- return FARM_POINT_STATUS_COLOR_DETECTORS.has(templateKey);
- }
- export function usesFarmTurbineStatusColor(templateKey = "") {
- return (
- FARM_STATUS_LEGEND_DETECTORS.has(templateKey) &&
- !FARM_POINT_STATUS_COLOR_DETECTORS.has(templateKey)
- );
- }
- /**
- * 静态偏航误差绝对值分档(度):
- * [0,3]、 (3,5]、 >5
- */
- export const YAW_ERROR_ABS_RANGES = Object.freeze([
- Object.freeze({
- key: "low",
- label: "[0,3]",
- color: DIAGNOSTIC_COLORS.blade2,
- }),
- Object.freeze({
- key: "mid",
- label: "(3,5]",
- color: DIAGNOSTIC_COLORS.warning,
- }),
- Object.freeze({
- key: "high",
- label: ">5",
- color: DIAGNOSTIC_COLORS.anomaly,
- }),
- ]);
- export function classifyYawErrorAbsRange(value) {
- const abs = Math.abs(Number(value));
- if (!Number.isFinite(abs)) return null;
- if (abs <= 3) return YAW_ERROR_ABS_RANGES[0];
- if (abs <= 5) return YAW_ERROR_ABS_RANGES[1];
- return YAW_ERROR_ABS_RANGES[2];
- }
- export function yawErrorAbsRangeColor(value) {
- return classifyYawErrorAbsRange(value)?.color || DIAGNOSTIC_COLORS.normal;
- }
- export function countYawErrorAbsRanges(values = []) {
- const counts = YAW_ERROR_ABS_RANGES.map(() => 0);
- values.forEach((value) => {
- const range = classifyYawErrorAbsRange(value);
- if (!range) return;
- const idx = YAW_ERROR_ABS_RANGES.findIndex(
- (item) => item.key === range.key,
- );
- if (idx >= 0) counts[idx] += 1;
- });
- return counts;
- }
- export const FARM_STATUS_NORMAL_LEGEND = "正常机组";
- /** 全场单值柱图:正常统一色、异常红,图例优先列出异常机组 */
- export function buildFarmStatusBarSeries({
- categories = [],
- values = [],
- extras = [],
- anomalyFlags = [],
- } = {}) {
- const flags = categories.map((_, idx) => Boolean(anomalyFlags[idx]));
- const abnormalNames = categories.filter((_, idx) => flags[idx]);
- const hasNormal = flags.some((flag) => !flag);
- const makeItem = (idx, color) => ({
- name: categories[idx],
- value: values[idx],
- extra: extras[idx],
- itemStyle: { color },
- });
- const series = [];
- if (hasNormal) {
- series.push({
- type: "bar",
- name: FARM_STATUS_NORMAL_LEGEND,
- color: DIAGNOSTIC_COLORS.normal,
- itemStyle: { color: DIAGNOSTIC_COLORS.normal },
- data: categories.map((_, idx) =>
- flags[idx] ? null : makeItem(idx, DIAGNOSTIC_COLORS.normal),
- ),
- barMaxWidth: 28,
- barGap: "-100%",
- });
- }
- abnormalNames.forEach((name) => {
- const hit = categories.indexOf(name);
- series.push({
- type: "bar",
- name,
- color: DIAGNOSTIC_COLORS.anomaly,
- itemStyle: { color: DIAGNOSTIC_COLORS.anomaly },
- data: categories.map((_, idx) =>
- idx === hit ? makeItem(idx, DIAGNOSTIC_COLORS.anomaly) : null,
- ),
- barMaxWidth: 28,
- barGap: "-100%",
- });
- });
- return {
- legendNames: [
- ...(hasNormal ? [FARM_STATUS_NORMAL_LEGEND] : []),
- ...abnormalNames,
- ],
- series,
- };
- }
- export function usesFarmStatusLegend(templateKey = "") {
- return FARM_STATUS_LEGEND_DETECTORS.has(templateKey);
- }
- function seriesText(rowOrLabel) {
- if (rowOrLabel && typeof rowOrLabel === "object") {
- return `${rowOrLabel.originalLabel || ""} ${rowOrLabel.label || ""}`;
- }
- return String(rowOrLabel || "");
- }
- /** 散点图内应叠加为折线的参考/功率曲线(非运行状态散点) */
- export function isOverlayCurveSeries(rowOrLabel) {
- if (rowOrLabel?.__shared) return true;
- const text = seriesText(rowOrLabel);
- if (/参考功率|理论功率|合同功率|参考曲线/.test(text)) return true;
- if (/近90日.*功率曲线|近90日功率曲线/.test(text)) return true;
- return false;
- }
- /** 全场总图跨机组去重保留一条的参考曲线 */
- export function isSharedOverlayCurveSeries(rowOrLabel) {
- const text = seriesText(rowOrLabel);
- if (/参考功率|理论功率|合同功率|参考曲线/.test(text)) return true;
- if (/近90日.*功率曲线|近90日功率曲线/.test(text)) return true;
- return false;
- }
- export function sharedOverlayCurveLabel(label) {
- const text = String(label || "");
- if (/近90日/.test(text)) return "近90日功率曲线";
- if (/合同功率/.test(text)) return "合同功率曲线";
- if (/参考功率|理论功率|参考曲线/.test(text)) return "参考功率曲线";
- return text;
- }
- export function isContractPowerCurve(rowOrLabel) {
- return /合同功率/.test(seriesText(rowOrLabel));
- }
- /** 单机图:合同功率曲线在散点/曲线下方,异常点压在正常点上面 */
- export const SINGLE_TURBINE_CONTRACT_Z = 1;
- export const SINGLE_TURBINE_SERIES_Z = 2;
- export const SINGLE_TURBINE_ANOMALY_Z = 8;
- export function diagnosticSingleSeriesZ(rowOrLabel) {
- const role = classifyDiagnosticSeries(rowOrLabel);
- if (role === "contract") return SINGLE_TURBINE_CONTRACT_Z;
- if (role === "anomaly" || role === "deload" || role === "shutdown") {
- return SINGLE_TURBINE_ANOMALY_Z;
- }
- return SINGLE_TURBINE_SERIES_Z;
- }
- function readSeriesCoord(point) {
- if (Array.isArray(point)) return point;
- if (Array.isArray(point?.value)) return point.value;
- return null;
- }
- /** 单机图里异常系列的坐标范围,用来把被 yrange 裁掉的异常点拉回坐标轴 */
- export function anomalySeriesAxisExtent(series = []) {
- let xMin = Infinity;
- let xMax = -Infinity;
- let yMin = Infinity;
- let yMax = -Infinity;
- let found = false;
- (series || []).forEach((item) => {
- if (classifyDiagnosticSeries(item?.name) !== "anomaly") return;
- (item.data || []).forEach((point) => {
- const coord = readSeriesCoord(point);
- if (!coord) return;
- const x = Number(coord[0]);
- const y = Number(coord[1]);
- if (Number.isFinite(x)) {
- if (x < xMin) xMin = x;
- if (x > xMax) xMax = x;
- found = true;
- }
- if (Number.isFinite(y)) {
- if (y < yMin) yMin = y;
- if (y > yMax) yMax = y;
- found = true;
- }
- });
- });
- if (!found) return null;
- return {
- xMin: Number.isFinite(xMin) ? xMin : null,
- xMax: Number.isFinite(xMax) ? xMax : null,
- yMin: Number.isFinite(yMin) ? yMin : null,
- yMax: Number.isFinite(yMax) ? yMax : null,
- };
- }
- /** 已锁定的轴范围若把异常点裁在外面,就扩到能包住这些点 */
- export function expandAxisToExtent(axis, dataMin, dataMax) {
- if (!axis || dataMin == null || dataMax == null) return axis;
- const min = Number(axis.min);
- const max = Number(axis.max);
- if (Number.isFinite(min) && dataMin < min) axis.min = dataMin;
- if (Number.isFinite(max) && dataMax > max) axis.max = dataMax;
- return axis;
- }
- /** 变桨-转速-功率协调:横轴固定从该值起展示 */
- export const PITCH_COORD_X_MIN = 1000;
- /** 变桨协调仍读取 yrange;横轴最小值固定,不跟 xrange 起点走 */
- export function usesDataAxisRange(templateKey) {
- return templateKey === "pitch_coord";
- }
- /**
- * 变桨协调横轴:最小固定为 1000。xrange 上限大于 1000 时沿用,否则最大值自适应。
- * @returns {boolean} 横轴最小值已锁定
- */
- export function applyPitchCoordXAxis(axis, xrange) {
- if (!axis) return false;
- axis.min = PITCH_COORD_X_MIN;
- const max = Array.isArray(xrange) ? Number(xrange[1]) : NaN;
- if (Number.isFinite(max) && max > PITCH_COORD_X_MIN) {
- axis.max = max;
- axis.scale = false;
- } else {
- axis.max = undefined;
- axis.scale = true;
- }
- return true;
- }
- /**
- * 有 xrange / yrange 就设为坐标轴 min/max。
- * @returns {boolean} 是否已锁定
- */
- export function lockAxisToRange(axis, range, convert) {
- if (!axis || !Array.isArray(range) || range.length < 2) return false;
- if (
- range[0] == null ||
- range[1] == null ||
- range[0] === "" ||
- range[1] === ""
- ) {
- return false;
- }
- const min = typeof convert === "function" ? convert(range[0]) : range[0];
- const max = typeof convert === "function" ? convert(range[1]) : range[1];
- if (min == null || max == null || min === "" || max === "") return false;
- if (typeof min === "number" && !Number.isFinite(min)) return false;
- if (typeof max === "number" && !Number.isFinite(max)) return false;
- axis.min = min;
- axis.max = max;
- axis.scale = false;
- return true;
- }
- export function classifyDiagnosticSeries(rowOrLabel) {
- const text = seriesText(rowOrLabel);
- if (/合同功率/.test(text)) return "contract";
- if (/桨叶\s*1/.test(text)) return "blade1";
- if (/桨叶\s*2/.test(text)) return "blade2";
- if (/桨叶\s*3/.test(text)) return "blade3";
- // 单机风功率散点运行状态(须先于「正常/异常」通用匹配)
- if (/停机/.test(text)) return "shutdown";
- if (/降载/.test(text) && !/累计降载|降载率|降载占比|降载判定/.test(text)) {
- return "deload";
- }
- if (/异常/.test(text) && !/正常/.test(text)) return "anomaly";
- if (/上限|下限|阈值|警戒|频繁/.test(text)) return "warning";
- if (/均值|平均|趋势/.test(text)) return "mean";
- if (isOverlayCurveSeries(rowOrLabel)) return "reference";
- if (/参考|理论|合同|基准|标准/.test(text) || rowOrLabel?.__shared) {
- return "reference";
- }
- if (/正常/.test(text)) return "normal";
- return "category";
- }
- /**
- * 系列颜色。options.pageTheme 时「停机」改用页面可见色(深色主题浅灰),
- * 报告与单机浅色主题不传该选项,保持 shutdown 深色不变。
- */
- export function diagnosticSeriesColor(rowOrLabel, index = 0, options = {}) {
- const role = classifyDiagnosticSeries(rowOrLabel);
- if (options.pageTheme && role === "shutdown") {
- return options.darkTheme
- ? PAGE_SHUTDOWN_COLORS.dark
- : PAGE_SHUTDOWN_COLORS.light;
- }
- if (DIAGNOSTIC_COLORS[role]) return DIAGNOSTIC_COLORS[role];
- const safeIndex = Math.abs(Number(index) || 0);
- return DIAGNOSTIC_CATEGORICAL_COLORS[
- safeIndex % DIAGNOSTIC_CATEGORICAL_COLORS.length
- ];
- }
- function paletteIndexOf(rowOrLabel, index = 0) {
- const turbineIndex = Number(rowOrLabel?.turbineIndex);
- if (Number.isFinite(turbineIndex)) return Math.abs(turbineIndex);
- return Math.abs(Number(index) || 0);
- }
- function turbineAnomalyFlag(rowOrLabel) {
- if (!rowOrLabel || typeof rowOrLabel !== "object") return null;
- // pointAnomaly 拆分后的行级 anomaly 优先于整机 extra.anomaly,避免正常点被整机标红
- if (typeof rowOrLabel.anomaly === "boolean") return rowOrLabel.anomaly;
- if (typeof rowOrLabel.extra?.anomaly === "boolean")
- return rowOrLabel.extra.anomaly;
- return null;
- }
- /**
- * 全场总图是否按「异常风机」上色。
- */
- export function isFarmAnomalyTurbine(rowOrLabel, templateKey = "") {
- const flagged = turbineAnomalyFlag(rowOrLabel);
- if (typeof flagged === "boolean") return flagged;
- return classifyDiagnosticSeries(rowOrLabel) === "anomaly";
- }
- export function farmStatusPaletteColor(
- rowOrLabel,
- index = 0,
- templateKey = "",
- ) {
- const palette = isFarmAnomalyTurbine(rowOrLabel, templateKey)
- ? FARM_ANOMALY_WARM_COLORS
- : FARM_NORMAL_COOL_COLORS;
- return palette[paletteIndexOf(rowOrLabel, index) % palette.length];
- }
- /**
- * 全场图颜色语义:
- * - 风功率曲线/散点、偏航分析 / 扭缆 / 变桨协调、气动 Cp/TSR:正常点蓝、异常点红(pointAnomaly);
- * - 变桨一致性、电能质量、运行状态、降载:异常机组整机红,正常机组统一蓝(无 pointAnomaly 时);有 pointAnomaly 时仍按点拆分后的行级 anomaly;
- * - 参考 / 阈值 / 均值保持语义色。
- * - 单机「降载黄 / 停机黑」仅用于单机风功率散点;全场总图一律红/蓝。
- */
- export function diagnosticFarmSeriesColor(
- rowOrLabel,
- index = 0,
- templateKey = "",
- ) {
- const role = classifyDiagnosticSeries(rowOrLabel);
- if (
- role === "contract" ||
- role === "reference" ||
- role === "warning" ||
- role === "mean"
- ) {
- return diagnosticSeriesColor(rowOrLabel, index);
- }
- if (templateKey === "wind_power_curve") {
- return isFarmAnomalyTurbine(rowOrLabel, templateKey)
- ? DIAGNOSTIC_COLORS.anomaly
- : DIAGNOSTIC_COLORS.normal;
- }
- if (usesFarmPointStatusColor(templateKey)) {
- return role === "anomaly" || role === "deload" || role === "shutdown"
- ? DIAGNOSTIC_COLORS.anomaly
- : DIAGNOSTIC_COLORS.normal;
- }
- if (usesFarmTurbineStatusColor(templateKey)) {
- // 报告拼接单机图时可能残留 label=降载/停机,按异常红而非单机黄/黑
- if (role === "deload" || role === "shutdown") {
- return DIAGNOSTIC_COLORS.anomaly;
- }
- return isFarmAnomalyTurbine(rowOrLabel, templateKey)
- ? DIAGNOSTIC_COLORS.anomaly
- : DIAGNOSTIC_COLORS.normal;
- }
- if (role === "deload" || role === "shutdown") {
- return diagnosticSeriesColor(rowOrLabel, index);
- }
- return farmStatusPaletteColor(rowOrLabel, index, templateKey);
- }
- export function farmTurbineLegendName(rowOrLabel) {
- const obj = rowOrLabel && typeof rowOrLabel === "object" ? rowOrLabel : null;
- let text = String(obj?.engineName || obj?.turbine || "").trim();
- if (!text) {
- text = String(obj?.label || rowOrLabel || "")
- .replace(/\s*(正常数据|异常点|正常|异常|降载|停机)$/u, "")
- .trim();
- }
- if (!text) return "";
- if (/^\d+$/.test(text)) return `${text}号`;
- return text;
- }
- export function farmStatusSeriesName(
- row,
- legendMode = "normal",
- templateKey = "",
- ) {
- const turbine = farmTurbineLegendName(row);
- if (!turbine) return row?.label || "";
- if (legendMode !== "anomaly") return turbine;
- const role = classifyDiagnosticSeries(row);
- const matchLegend = usesFarmTurbineStatusColor(templateKey)
- ? isFarmAnomalyTurbine(row, templateKey) ||
- role === "deload" ||
- role === "shutdown"
- : role === "anomaly" || role === "deload" || role === "shutdown";
- return matchLegend ? turbine : `${turbine} 正常`;
- }
- export function pickFarmStatusLegend(rows = [], templateKey = "") {
- const map = new Map();
- let hasNormal = false;
- (rows || []).forEach((row) => {
- if (!row || row.__shared) return;
- const role = classifyDiagnosticSeries(row);
- if (
- role === "contract" ||
- role === "reference" ||
- role === "warning" ||
- role === "mean"
- ) {
- return;
- }
- const name = farmTurbineLegendName(row);
- if (!name) return;
- const anomalous = usesFarmTurbineStatusColor(templateKey)
- ? isFarmAnomalyTurbine(row, templateKey) ||
- role === "deload" ||
- role === "shutdown"
- : role === "anomaly" ||
- role === "deload" ||
- role === "shutdown" ||
- isFarmAnomalyTurbine(row, templateKey);
- if (usesFarmPointStatusColor(templateKey)) {
- if (role === "normal") hasNormal = true;
- } else if (!anomalous) {
- hasNormal = true;
- }
- map.set(name, Boolean(map.get(name)) || anomalous);
- });
- const entries = [...map.entries()];
- const abnormal = entries.filter(([, flag]) => flag).map(([name]) => name);
- if (abnormal.length) {
- return {
- mode: "anomaly",
- names: hasNormal ? [FARM_STATUS_NORMAL_LEGEND, ...abnormal] : abnormal,
- };
- }
- return { mode: "normal", names: entries.map(([name]) => name) };
- }
- export function buildFarmNormalLegendSeries() {
- return {
- name: FARM_STATUS_NORMAL_LEGEND,
- type: "scatter",
- data: [],
- symbol: "circle",
- color: DIAGNOSTIC_COLORS.normal,
- itemStyle: { color: DIAGNOSTIC_COLORS.normal },
- tooltip: { show: false },
- silent: true,
- z: 0,
- };
- }
- export function ensureFarmNormalLegendSeries(series = [], legendPick) {
- if (!legendPick?.names?.includes(FARM_STATUS_NORMAL_LEGEND)) return series;
- if (series.some((item) => item.name === FARM_STATUS_NORMAL_LEGEND)) {
- return series;
- }
- return [buildFarmNormalLegendSeries(), ...series];
- }
- /** 全场散点统一点径:正常点与异常点一样大,只靠颜色和绘制顺序区分 */
- export const FARM_SCATTER_SYMBOL_SIZE = 4;
- export function diagnosticFarmSymbolSize(
- _rowOrLabel,
- _pointCount = 0,
- _templateKey = "",
- ) {
- return FARM_SCATTER_SYMBOL_SIZE;
- }
- export function diagnosticFarmStyle(rowOrLabel, templateKey = "") {
- const role = classifyDiagnosticSeries(rowOrLabel);
- if (role === "contract") {
- return DIAGNOSTIC_FARM_STYLES.contract;
- }
- if (role === "reference" || role === "warning" || role === "mean") {
- return Object.freeze({
- ...(DIAGNOSTIC_FARM_STYLES[role] || DIAGNOSTIC_FARM_STYLES.category),
- z: 30,
- });
- }
- const base = DIAGNOSTIC_FARM_STYLES[role] || DIAGNOSTIC_FARM_STYLES.category;
- const pointAnomalyRole =
- role === "anomaly" || role === "deload" || role === "shutdown";
- const farmAnomaly =
- isFarmAnomalyTurbine(rowOrLabel, templateKey) ||
- role === "deload" ||
- role === "shutdown";
- if (pointAnomalyRole) {
- const anomaly = DIAGNOSTIC_FARM_STYLES.anomaly;
- return {
- ...base,
- z: anomaly.z,
- lineWidth: Math.max(base.lineWidth, anomaly.lineWidth),
- lineOpacity: anomaly.lineOpacity,
- pointOpacity: 1,
- };
- }
- if (usesFarmTurbineStatusColor(templateKey) && farmAnomaly) {
- const anomaly = DIAGNOSTIC_FARM_STYLES.anomaly;
- return {
- ...base,
- z: anomaly.z,
- lineWidth: Math.max(base.lineWidth, anomaly.lineWidth),
- lineOpacity: anomaly.lineOpacity,
- pointOpacity: anomaly.pointOpacity,
- };
- }
- return base;
- }
- /** 全场总图绘制顺序:异常点压在正常点上面,仍与坐标轴同一层,避免独立 zlevel 丢点 */
- export function farmSeriesPaintOrder(rowOrLabel, templateKey = "") {
- const style = diagnosticFarmStyle(rowOrLabel, templateKey);
- const role = classifyDiagnosticSeries(rowOrLabel);
- const lifted =
- role === "anomaly" ||
- role === "deload" ||
- role === "shutdown" ||
- (usesFarmTurbineStatusColor(templateKey) &&
- isFarmAnomalyTurbine(rowOrLabel, templateKey) &&
- role !== "normal" &&
- role !== "contract" &&
- role !== "reference" &&
- role !== "warning" &&
- role !== "mean");
- return {
- z: lifted ? Math.max(Number(style.z) || 0, 20) : Number(style.z) || 1,
- z2: lifted ? 30 : 0,
- zlevel: 0,
- };
- }
- export function diagnosticBladeStyle(rowOrLabel) {
- const role = classifyDiagnosticSeries(rowOrLabel);
- if (role === "blade1") {
- return { symbol: "rect", lineType: "dashed", showSymbol: true };
- }
- if (role === "blade2") {
- return { symbol: "circle", lineType: "solid", showSymbol: true };
- }
- if (role === "blade3") {
- return { symbol: "triangle", lineType: "dotted", showSymbol: true };
- }
- return null;
- }
- export function diagnosticSeriesSymbol(rowOrLabel, fallback = "circle") {
- const role = classifyDiagnosticSeries(rowOrLabel);
- if (role === "blade1") return "rect";
- if (role === "blade2") return "circle";
- if (role === "blade3") return "triangle";
- return fallback || "circle";
- }
- export function diagnosticLineType(rowOrLabel) {
- const role = classifyDiagnosticSeries(rowOrLabel);
- if (role === "contract") return "solid";
- if (role === "warning" || role === "reference" || role === "mean") {
- return "dashed";
- }
- if (role === "blade1") return "dashed";
- if (role === "blade3") return "dotted";
- return "solid";
- }
|