Compare commits
2 Commits
5f1708424f
...
5fc70cd610
| Author | SHA1 | Date |
|---|---|---|
|
|
5fc70cd610 | |
|
|
4c0a272df2 |
|
|
@ -150,23 +150,38 @@ export class MenuService {
|
||||||
return Array.from(entities);
|
return Array.from(entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private normalizeIdPart(part: string): string {
|
||||||
|
return part
|
||||||
|
.replace(/\$/g, '_') // Заменяем $ на _
|
||||||
|
.replace(/[^a-zA-Z0-9-_]/g, ''); // Удаляем другие спецсимволы
|
||||||
|
}
|
||||||
|
|
||||||
private generateModuleItems(
|
private generateModuleItems(
|
||||||
device: string,
|
device: string,
|
||||||
seriesData: { metric: string; labels: Record<string, string> }[],
|
seriesData: { metric: string; labels: Record<string, string> }[],
|
||||||
metadataMap: Map<string, string>
|
metadataMap: Map<string, string>
|
||||||
): MenuItem[] {
|
): MenuItem[] {
|
||||||
const modules = new Set<string>();
|
const modules = new Map<string, string>(); // source_id -> display name
|
||||||
|
|
||||||
seriesData.forEach(({ labels }) => {
|
seriesData.forEach(({ labels }) => {
|
||||||
if (labels.device === device && labels.source_id) {
|
if (labels.device === device && labels.source_id) {
|
||||||
modules.add(labels.source_id);
|
const sourceId = labels.source_id;
|
||||||
|
let displayName = sourceId;
|
||||||
|
|
||||||
|
if (sourceId.startsWith('module$')) {
|
||||||
|
displayName = `Module ${sourceId.split('$')[1]}`;
|
||||||
|
} else if (sourceId.startsWith('port$')) {
|
||||||
|
displayName = `Port ${sourceId.split('$')[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
modules.set(sourceId, displayName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Array.from(modules).map(module => ({
|
return Array.from(modules.entries()).map(([sourceId, displayName]) => ({
|
||||||
id: `module_${device}_${module}`,
|
id: `module_${device}_${sourceId}`,
|
||||||
title: `Module ${module.replace('module$', '')}`,
|
title: displayName,
|
||||||
items: this.generateMetricItems(device, module, seriesData, metadataMap),
|
items: this.generateMetricItems(device, sourceId, seriesData, metadataMap),
|
||||||
isDynamic: true
|
isDynamic: true
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -177,24 +192,45 @@ export class MenuService {
|
||||||
seriesData: { metric: string; labels: Record<string, string> }[],
|
seriesData: { metric: string; labels: Record<string, string> }[],
|
||||||
metadataMap: Map<string, string>
|
metadataMap: Map<string, string>
|
||||||
): MenuItem[] {
|
): MenuItem[] {
|
||||||
|
// Фильтруем метрики для текущего устройства и модуля
|
||||||
const filtered = seriesData.filter(
|
const filtered = seriesData.filter(
|
||||||
({ labels }) => labels.device === device && labels.source_id === module
|
({ labels }) => labels.device === device && labels.source_id === module
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Получаем уникальные имена метрик
|
||||||
const uniqueMetrics = new Set(filtered.map(entry => entry.metric));
|
const uniqueMetrics = new Set(filtered.map(entry => entry.metric));
|
||||||
|
|
||||||
|
// Нормализуем идентификаторы
|
||||||
|
const normalizeIdPart = (part: string): string => {
|
||||||
|
return part
|
||||||
|
.replace(/\$/g, '_') // Заменяем $ на _
|
||||||
|
.replace(/[^a-zA-Z0-9-_]/g, '') // Удаляем другие спецсимволы
|
||||||
|
.replace(/_+/g, '_') // Заменяем множественные _ на один
|
||||||
|
.replace(/^_|_$/g, ''); // Удаляем _ в начале и конце
|
||||||
|
};
|
||||||
|
|
||||||
|
const safeDevice = normalizeIdPart(device);
|
||||||
|
const safeModule = normalizeIdPart(module);
|
||||||
|
|
||||||
|
// Формируем пункты меню
|
||||||
return Array.from(uniqueMetrics).map(metric => {
|
return Array.from(uniqueMetrics).map(metric => {
|
||||||
const description = metadataMap.get(metric) || metric;
|
const description = metadataMap.get(metric) || metric;
|
||||||
|
const safeMetric = normalizeIdPart(metric);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: `metric_${device}_${module}_${metric}`,
|
id: `metric_${safeDevice}_${safeModule}_${safeMetric}`, // Безопасный ID
|
||||||
title: description,
|
title: description,
|
||||||
metric,
|
metric,
|
||||||
filters: {
|
filters: {
|
||||||
device,
|
device, // Оригинальное значение device
|
||||||
source_id: module
|
source_id: module // Оригинальное значение source_id
|
||||||
},
|
},
|
||||||
isDynamic: true
|
isDynamic: true,
|
||||||
|
// Сохраняем оригинальные значения для отладки
|
||||||
|
meta: {
|
||||||
|
originalDevice: device,
|
||||||
|
originalModule: module
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,9 +117,8 @@ export class MetricsGateway implements OnGatewayInit, OnGatewayConnection, OnGat
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSubscriptionKey(metric: string, filters: Record<string, string>): string {
|
private getSubscriptionKey(metric: string, filters: Record<string, string>): string {
|
||||||
// Создаём уникальный ключ на основе метрики и фильтров
|
|
||||||
const filterKeys = Object.keys(filters).sort();
|
const filterKeys = Object.keys(filters).sort();
|
||||||
const filterString = filterKeys.map(k => `${k}=${filters[k]}`).join('&');
|
const filterString = filterKeys.map(k => `${k}=${encodeURIComponent(filters[k])}`).join('&');
|
||||||
return `${metric}${filterString ? `?${filterString}` : ''}`;
|
return `${metric}${filterString ? `?${filterString}` : ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,7 +139,6 @@ export class MetricsGateway implements OnGatewayInit, OnGatewayConnection, OnGat
|
||||||
metric,
|
metric,
|
||||||
interval,
|
interval,
|
||||||
(data) => {
|
(data) => {
|
||||||
// Отправляем только подписчикам этой конкретной метрики с фильтрами
|
|
||||||
this.server.emit('metrics-data', {
|
this.server.emit('metrics-data', {
|
||||||
metric: subscriptionKey,
|
metric: subscriptionKey,
|
||||||
data
|
data
|
||||||
|
|
|
||||||
|
|
@ -112,9 +112,7 @@ export class PrometheusService {
|
||||||
const filterParts = Object.entries(filters)
|
const filterParts = Object.entries(filters)
|
||||||
.filter(([_, value]) => value !== undefined && value !== null && value !== "")
|
.filter(([_, value]) => value !== undefined && value !== null && value !== "")
|
||||||
.map(([key, value]) => {
|
.map(([key, value]) => {
|
||||||
if (key === 'source_id' && !value.startsWith('module$')) {
|
// Убираем автоматическое добавление "module$" для source_id
|
||||||
return `${key}="module$${value}"`;
|
|
||||||
}
|
|
||||||
return `${key}="${value}"`;
|
return `${key}="${value}"`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue