function drawReq(show, req){
if (req){
if (!show){
var name = 'req_'+req.requestId
while (viewer.dataSources.remove(viewer.dataSources.getByName(name)[0])) {}
} else{
var u = '/requests/req-with-cells/'+req.requestId
fetch(u, {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
})
.then((response)=>response.json())
.then((json) =>{
viewer.dataSources.add(Cesium.CzmlDataSource.load(json));
})
}
}
}
function drawOrbit(show, req, startInput, endInput){
if (req){
if (!show){
var name = 'sat_'+req.noradId
while (viewer.dataSources.remove(viewer.dataSources.getByName(name)[0])) {}
checkModels();
} else{
const inputOrb = document.getElementById('toggleOrbit');
let valueOrb = inputOrb.checked;
const inputFl = document.getElementById('toggleFL');
let valueFl = inputFl.checked;
const inputSw = document.getElementById('toggleSwath');
let valueSw = inputSw.checked;
var sceneMode = (viewer.scene.mode == Cesium.SceneMode.SCENE2D);
var u = '/requests/sat/'+req.noradId+
'?view='+sceneMode+'&orbit='+valueOrb+
'&sw='+valueSw+
'&fl='+valueFl
if (startInput && endInput) {
u += '&tn=' + encodeURIComponent(startInput) + '&tk=' + encodeURIComponent(endInput)
}
console.log(u)
fetch(u, {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
})
.then((response)=>response.json())
.then((json) =>{
console.log(json)
viewer.dataSources.add(Cesium.CzmlDataSource.load(json));
checkModels();
})
}
}
}
function drawSt(show, req){
if (req){
if (!show){
var name = 'station_'+req.id
while (viewer.dataSources.remove(viewer.dataSources.getByName(name)[0])) {}
checkModels();
} else{
var sceneMode = (viewer.scene.mode == Cesium.SceneMode.SCENE2D);
var u = '/requests/station/'+req.id+'?view='+sceneMode+
'&orbitHeightKm='+encodeURIComponent(selectedStationOrbitHeightKm())
fetch(u, {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
})
.then(async (response) => {
const text = await response.text();
let payload;
try {
payload = text ? JSON.parse(text) : [];
} catch (error) {
payload = { message: text || `HTTP error! status: ${response.status}` };
}
if (!response.ok) {
throw new Error(payload.error || payload.message || `HTTP error! status: ${response.status}`);
}
return payload;
})
.then((json) =>{
if (!Array.isArray(json) || !json[0] || json[0].id !== 'document') {
throw new Error('Некорректный CZML для плана спутника');
}
viewer.dataSources.add(Cesium.CzmlDataSource.load(json));
checkModels();
})
.catch(error => {
console.error('Ошибка при загрузке плана спутника:', error);
alert(error.message);
})
}
}
}
function selectedStationOrbitHeightKm() {
const input = document.getElementById('stationOrbitHeightKm');
const value = Number(input?.value || 500);
if (!Number.isFinite(value) || value <= 0) {
if (input) input.value = '500';
return 500;
}
return value;
}
function drawSat(show, req, startInput, endInput, runId){
if (req){
if (!show){
var name = 'plan_'+req.noradId
while (viewer.dataSources.remove(viewer.dataSources.getByName(name)[0])) {}
checkModels();
} else{
// const url = `/requests/req-with-covs/${requestId}?tn=${startInput}&tk=${endInput}&cov=`+cover+sign;
let u = '/requests/plan/'+req.noradId+'?tn='+startInput+'&tk='+endInput
if (runId) {
u += '&runId=' + encodeURIComponent(runId)
}
console.log(u)
fetch(u, {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
})
.then((response)=>response.json())
.then((json) =>{
viewer.dataSources.add(Cesium.CzmlDataSource.load(json));
checkModels();
})
}
}
}
// выбрать все НИП/ППИ
function selectAllRequests(){
$('input:checkbox[name="request"]:visible:not(:checked)').each(function(){$(this).trigger('click')});
}
// скрыть все НИП/ППИ
function deselectAllRequests(){
$('input:checkbox[name="request"]:checked').each(function(){$(this).trigger('click')});
}
// Функция для отображения тепловой карты
function showHeatmap(importance, countLat, countLong) {
if (!window.viewer) return;
hideHeatmap();
var params = new URLSearchParams({ importance: importance });
if (countLat) {
params.append('countLat', countLat);
}
if (countLong) {
params.append('countLong', countLong);
}
var u = '/requests?' + params.toString()
fetch(u, {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
})
.then((response)=>response.json())
.then((json) =>{
viewer.dataSources.add(Cesium.CzmlDataSource.load(json));
})
}
// Функция для скрытия тепловой карты
function hideHeatmap() {
if (!window.viewer) return;
// Удаляем слой тепловой карты, если он существует
while (viewer.dataSources.remove(viewer.dataSources.getByName('heatMap')[0])) {}
}
function useSceneMode(sceneMode){
if (sceneMode == Cesium.SceneMode.SCENE2D){
console.log('2D')
} else
console.log('3D')
}
function checkModels(){
var sceneMode = (viewer.scene.mode == Cesium.SceneMode.SCENE2D);
viewer.dataSources._dataSources.forEach(dataSource => {
dataSource._entityCollection._entities._array.forEach( entity => {
if (entity.id.includes('station_')){
entity.model.show=!sceneMode
entity.billboard.show=sceneMode
} else if (entity.id.includes('orbit_')){
entity.model.show=!sceneMode
entity.point.show=sceneMode
}
})
});
}
function handleOrbits(show){
viewer.dataSources._dataSources.forEach(dataSource => {
dataSource._entityCollection._entities._array.forEach( entity => {
if (entity.id.includes('orbit_')){
entity.path.show=show
}
})
});
}
function handleSw(show){
viewer.dataSources._dataSources.forEach(dataSource => {
dataSource._entityCollection._entities._array.forEach( entity => {
if (entity.id.includes('swath_')){
entity.path.show=show
}
})
});
}
function handleFl(show){
viewer.dataSources._dataSources.forEach(dataSource => {
dataSource._entityCollection._entities._array.forEach( entity => {
if (entity.id.includes('flight_line_')){
entity.path.show=show
entity.point.show=show
}
})
});
}
// Глобальные переменные для хранения данных слотов
let currentSlotsData = [];
let currentRequestId = null;
let currentSlotsContext = null;
let slotsSortConfig = {
column: 'tn',
direction: 'asc'
};
function removeDataSourceByName(dataSourceName) {
let dataSource = viewer.dataSources.getByName(dataSourceName)[0];
while (dataSource && viewer.dataSources.remove(dataSource)) {
dataSource = viewer.dataSources.getByName(dataSourceName)[0];
}
}
function getCurrentSlotsDataSource() {
if (!currentSlotsContext || !currentSlotsContext.dataSourceName) {
return null;
}
return viewer.dataSources.getByName(currentSlotsContext.dataSourceName)[0] || null;
}
function getCoverageSchemeDataSourceName(requestId) {
return `req_cov_scheme_${requestId}`;
}
function setSlotsContext(context) {
currentSlotsContext = context;
updateSlotsActionButtons();
}
function updateSlotsActionButtons() {
const isRequestContext = currentSlotsContext && currentSlotsContext.type === 'request';
const bookButton = document.getElementById('bookSlotsBtn');
const cancelButton = document.getElementById('cancelBookingBtn');
if (bookButton) {
bookButton.style.display = isRequestContext ? '' : 'none';
}
if (cancelButton) {
cancelButton.style.display = isRequestContext ? '' : 'none';
}
}
function getSatelliteBookedSlotsDataSourceName(satelliteId) {
return `sat_booked_${satelliteId}`;
}
function getSatelliteSlotsDataSourceName(satelliteId) {
return `sat_slots_${satelliteId}`;
}
function buildSlotEntityId(slot, index) {
if (slot.entityId) {
return slot.entityId;
}
const satelliteId = slot.satelliteId ?? 'unknown';
const cycle = slot.cycle ?? slot.revolution ?? 0;
const slotNumber = slot.slotNumber
?? (Array.isArray(slot.slotIds) && slot.slotIds.length > 0 ? slot.slotIds[0] : index);
return `slot_${satelliteId}_${cycle}_${slotNumber}`;
}
function normalizeRequestSlots(slots) {
return (slots || []).map((slot, index) => ({
...slot,
index: index,
entityId: buildSlotEntityId(slot, index)
}));
}
function normalizeSatelliteBookedSlots(slots) {
return (slots || []).map((slot, index) => ({
...slot,
cycle: slot.revolution ?? 0,
slotNumber: Array.isArray(slot.slotIds) && slot.slotIds.length > 0 ? slot.slotIds[0] : index + 1,
state: 'BOOKED',
index: index,
entityId: buildSlotEntityId({
...slot,
cycle: slot.revolution ?? 0,
slotNumber: Array.isArray(slot.slotIds) && slot.slotIds.length > 0 ? slot.slotIds[0] : index + 1
}, index)
}));
}
function normalizeSatelliteSlots(slots) {
return (slots || []).map((slot, index) => ({
...slot,
index: index,
entityId: buildSlotEntityId(slot, index)
}));
}
function normalizeCoverageSchemeModes(modes) {
return (modes || []).map((mode, index) => ({
...mode,
index: index,
sequence: mode.sequence ?? (index + 1),
cycle: mode.cycle ?? mode.sequence ?? (index + 1),
tn: mode.tn ?? mode.timeStart,
tk: mode.tk ?? mode.timeEnd,
roll: mode.roll ?? mode.rollDegrees,
contour: mode.contour ?? mode.contourWkt,
entityId: mode.entityId ?? `coverage_mode_${mode.satelliteId ?? 'unknown'}_${mode.sequence ?? index + 1}`
}));
}
function parsePolygonWktToDegreesArray(contourWkt) {
if (typeof contourWkt !== 'string') {
return null;
}
const trimmed = contourWkt.trim();
if (!trimmed.toUpperCase().startsWith('POLYGON')) {
return null;
}
const body = trimmed
.replace(/^POLYGON\s*\(\(/i, '')
.replace(/\)\)\s*$/i, '');
const outerRingText = body.split('),(')[0];
const coordinates = outerRingText
.split(',')
.map(pair => pair.trim().split(/\s+/))
.map(parts => ({
longitude: Number(parts[0]),
latitude: Number(parts[1])
}))
.filter(point => Number.isFinite(point.longitude) && Number.isFinite(point.latitude));
if (coordinates.length < 3) {
return null;
}
const degrees = [];
coordinates.forEach(point => {
degrees.push(point.longitude, point.latitude, 0.0);
});
return degrees;
}
function closeDegreesArrayHeights(positions) {
if (!Array.isArray(positions) || positions.length < 9) {
return positions;
}
const first = positions.slice(0, 3);
const last = positions.slice(-3);
const isClosed = first.length === last.length && first.every((value, index) => value === last[index]);
return isClosed ? positions : positions.concat(first);
}
function getSatelliteCesiumColor(satellite) {
const red = Number(satellite && satellite.red);
const green = Number(satellite && satellite.green);
const blue = Number(satellite && satellite.blue);
return Cesium.Color.fromBytes(
Number.isFinite(red) ? red : 255,
Number.isFinite(green) ? green : 140,
Number.isFinite(blue) ? blue : 0,
255
);
}
function createSatelliteBookedSlotsDataSource(dataSourceName, satellite, slots) {
const dataSource = new Cesium.CustomDataSource(dataSourceName);
const color = getSatelliteCesiumColor(satellite);
normalizeSatelliteBookedSlots(slots).forEach(slot => {
const positions = parsePolygonWktToDegreesArray(slot.contour);
if (!positions) {
console.warn('Не удалось разобрать контур забронированного слота', slot);
return;
}
dataSource.entities.add({
id: slot.entityId,
name: `Забронированный слот КА ${slot.satelliteId}`,
description: [
`Спутник: ${slot.satelliteId ?? '-'}`,
`Начало: ${formatDateTime(slot.tn)}`,
`Окончание: ${formatDateTime(slot.tk)}`,
`Длительность: ${formatDuration(calculateDuration(slot.tn, slot.tk))}`,
`Виток: ${slot.revolution ?? '-'}`,
`Слоты: ${(slot.slotIds || []).join(', ') || '-'}`
].join('
'),
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(positions),
material: color.withAlpha(0.28),
outline: true,
outlineColor: color,
outlineWidth: 2.0
},
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights(closeDegreesArrayHeights(positions)),
width: 2.0,
material: color
}
});
});
return dataSource;
}
function createSatelliteSlotsDataSource(dataSourceName, satellite, slots) {
const dataSource = new Cesium.CustomDataSource(dataSourceName);
const color = getSatelliteCesiumColor(satellite);
normalizeSatelliteSlots(slots).forEach(slot => {
const positions = parsePolygonWktToDegreesArray(slot.contour);
if (!positions) {
console.warn('Не удалось разобрать контур слота', slot);
return;
}
dataSource.entities.add({
id: slot.entityId,
name: `Слот КА ${slot.satelliteId}`,
description: [
`Спутник: ${slot.satelliteId ?? '-'}`,
`Цикл: ${slot.cycle ?? '-'}`,
`Начало: ${formatDateTime(slot.tn)}`,
`Окончание: ${formatDateTime(slot.tk)}`,
`Длительность: ${formatDuration(calculateDuration(slot.tn, slot.tk))}`,
`Виток: ${slot.revolution ?? '-'}`,
`Номер слота: ${slot.slotNumber ?? '-'}`
].join('
'),
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(positions),
material: color.withAlpha(0.18),
outline: true,
outlineColor: color,
outlineWidth: 2.0
},
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights(closeDegreesArrayHeights(positions)),
width: 2.0,
material: color
}
});
});
return dataSource;
}
function processAndDisplaySatelliteBookedSlots(slots, satellite) {
currentSlotsData = normalizeSatelliteBookedSlots(slots);
currentRequestId = null;
setSlotsContext({
type: 'satellite-booked',
key: String(satellite.noradId),
dataSourceName: getSatelliteBookedSlotsDataSourceName(satellite.noradId)
});
document.getElementById('slotsTitle').textContent =
`Забронированные слоты спутника "${satellite.name}" (${currentSlotsData.length} интервалов)`;
document.getElementById('slotsContainer').style.display = 'block';
if (currentSlotsData.length === 0) {
showEmptySlotsMessage('Для выбранного временного диапазона забронированные слоты не найдены');
updateSlotsSummary();
return;
}
renderSlotsTable();
updateSlotsSummary();
}
function processAndDisplaySatelliteSlots(slots, satellite) {
currentSlotsData = normalizeSatelliteSlots(slots);
currentRequestId = null;
setSlotsContext({
type: 'satellite-slots',
key: String(satellite.noradId),
dataSourceName: getSatelliteSlotsDataSourceName(satellite.noradId)
});
document.getElementById('slotsTitle').textContent =
`Слоты спутника "${satellite.name}" (${currentSlotsData.length} интервалов)`;
document.getElementById('slotsContainer').style.display = 'block';
if (currentSlotsData.length === 0) {
showEmptySlotsMessage('Для выбранного временного диапазона слоты не найдены');
updateSlotsSummary();
return;
}
updateSlotsTableHeaders();
renderSlotsTable();
updateSlotsSummary();
}
function drawSatelliteBookedSlots(show, satellite, startInput, endInput) {
if (!satellite) {
return;
}
const dataSourceName = getSatelliteBookedSlotsDataSourceName(satellite.noradId);
if (!show) {
removeDataSourceByName(dataSourceName);
if (currentSlotsContext
&& currentSlotsContext.type === 'satellite-booked'
&& currentSlotsContext.key === String(satellite.noradId)) {
clearSlotsTable();
}
return;
}
const params = new URLSearchParams({
timeStart: startInput,
timeStop: endInput
});
fetch(`/api/satellites/${satellite.noradId}/booked-slots?${params.toString()}`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
})
.then(async response => {
if (!response.ok) {
const rawError = await response.text();
let errorMessage = rawError;
try {
const parsedError = JSON.parse(rawError);
errorMessage = parsedError.error || parsedError.message || rawError;
} catch (_) {}
throw new Error(`HTTP error! status: ${response.status} message: ${errorMessage}`);
}
return response.json();
})
.then(slots => {
removeDataSourceByName(dataSourceName);
viewer.dataSources.add(createSatelliteBookedSlotsDataSource(dataSourceName, satellite, slots));
processAndDisplaySatelliteBookedSlots(slots, satellite);
})
.catch(error => {
console.error('Ошибка при загрузке забронированных слотов спутника:', error);
alert(error.message);
});
}
function drawSatelliteSlots(show, satellite, startInput, endInput) {
if (!satellite) {
return;
}
const dataSourceName = getSatelliteSlotsDataSourceName(satellite.noradId);
if (!show) {
removeDataSourceByName(dataSourceName);
if (currentSlotsContext
&& currentSlotsContext.type === 'satellite-slots'
&& currentSlotsContext.key === String(satellite.noradId)) {
clearSlotsTable();
}
return;
}
const params = new URLSearchParams({
timeStart: startInput,
timeStop: endInput
});
fetch(`/api/satellites/${satellite.noradId}/slots?${params.toString()}`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
})
.then(async response => {
if (!response.ok) {
const rawError = await response.text();
let errorMessage = rawError;
try {
const parsedError = JSON.parse(rawError);
errorMessage = parsedError.error || parsedError.message || rawError;
} catch (_) {}
throw new Error(`HTTP error! status: ${response.status} message: ${errorMessage}`);
}
return response.json();
})
.then(slots => {
removeDataSourceByName(dataSourceName);
viewer.dataSources.add(createSatelliteSlotsDataSource(dataSourceName, satellite, slots));
processAndDisplaySatelliteSlots(slots, satellite);
})
.catch(error => {
console.error('Ошибка при загрузке слотов спутника:', error);
alert(error.message);
});
}
function drawReqCov(show, req, startInput, endInput, node, cover, satellites, sun) {
if (req) {
const requestId = req.requestId;
const dataSourceName = 'req_cov_' + requestId;
if (!show) {
// Удаляем данные покрытия с карты
removeDataSourceByName(dataSourceName);
// Если это текущая отображаемая заявка, очищаем таблицу слотов
if (currentRequestId === requestId) {
clearSlotsTable();
}
} else {
const params = new URLSearchParams({
tn: startInput,
tk: endInput,
cov: cover,
sun: sun
});
if (node == 1) {
params.append('sign', 'ASC');
} else if (node == 2) {
params.append('sign', 'DESC');
}
if (Array.isArray(satellites)) {
satellites.forEach(satelliteId => params.append('satellites', satelliteId));
}
params.append('sun', sun)
const url = `/requests/req-with-covs/${requestId}?${params.toString()}`;
console.log('Запрос покрытия:', url);
fetch(url, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
})
.then(async response => {
if (!response.ok) {
const rawError = await response.text();
let errorMessage = rawError;
try {
const parsedError = JSON.parse(rawError);
errorMessage = parsedError.error || parsedError.message || rawError;
} catch (_) {}
throw new Error(`HTTP error! status: ${response.status} message: ${errorMessage}`);
}
return response.json();
})
.then(json => {
// Отображаем покрытие на карте
if (json.cov && json.cov.length > 0) {
viewer.dataSources.add(Cesium.CzmlDataSource.load(json.cov));
}
// Обрабатываем и отображаем слоты в таблице
if (json.slots && json.slots.length > 0) {
processAndDisplaySlots(json.slots, req);
} else {
showEmptySlotsMessage('Нет интервалов покрытия для выбранного временного диапазона');
}
})
.catch(error => {
console.error('Ошибка при загрузке данных покрытия:', error);
alert(error.message);
});
}
}
}
function drawReqCoverageScheme(show, req, startInput, endInput, satellites, options) {
if (!req) {
return;
}
const requestId = req.requestId;
const dataSourceName = getCoverageSchemeDataSourceName(requestId);
if (!show) {
removeDataSourceByName(dataSourceName);
if (currentSlotsContext
&& currentSlotsContext.type === 'coverage-scheme'
&& currentSlotsContext.key === requestId) {
clearSlotsTable();
}
return;
}
if (!Array.isArray(satellites) || satellites.length === 0) {
alert('Для расчета схемы покрытия выберите хотя бы один спутник');
return;
}
const params = new URLSearchParams({
tn: startInput,
tk: endInput
});
satellites.forEach(satelliteId => params.append('satellites', satelliteId));
if (options) {
if (options.rollStepDegrees !== null && options.rollStepDegrees !== undefined) {
params.append('rollStepDegrees', options.rollStepDegrees);
}
if (options.minimumTechnologyOverlap !== null && options.minimumTechnologyOverlap !== undefined) {
params.append('minimumTechnologyOverlap', options.minimumTechnologyOverlap);
}
if (options.strictContinuousCoverage !== null && options.strictContinuousCoverage !== undefined) {
params.append('strictContinuousCoverage', options.strictContinuousCoverage);
}
if (options.allowPartialCoverage !== null && options.allowPartialCoverage !== undefined) {
params.append('allowPartialCoverage', options.allowPartialCoverage);
}
if (options.orientationToleranceDegrees !== null && options.orientationToleranceDegrees !== undefined) {
params.append('orientationToleranceDegrees', options.orientationToleranceDegrees);
}
if (options.includeDebugInfo !== null && options.includeDebugInfo !== undefined) {
params.append('includeDebugInfo', options.includeDebugInfo);
}
}
fetch(`/requests/req-with-coverage-scheme/${requestId}?${params.toString()}`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(async response => {
if (!response.ok) {
const rawError = await response.text();
let errorMessage = rawError;
try {
const parsedError = JSON.parse(rawError);
errorMessage = parsedError.error || parsedError.message || rawError;
} catch (_) {}
throw new Error(`HTTP error! status: ${response.status} message: ${errorMessage}`);
}
return response.json();
})
.then(json => {
removeDataSourceByName(dataSourceName);
if (json.cov && json.cov.length > 0) {
viewer.dataSources.add(Cesium.CzmlDataSource.load(json.cov));
}
if (json.slots && json.slots.length > 0) {
processAndDisplayCoverageSchemeModes(json.slots, req);
} else {
document.getElementById('slotsTitle').textContent =
`Схема покрытия заявки "${req.name}"`;
setSlotsContext({
type: 'coverage-scheme',
key: requestId,
dataSourceName: dataSourceName
});
updateSlotsTableHeaders();
showEmptySlotsMessage('Для выбранного временного диапазона режимы схемы покрытия не найдены');
}
})
.catch(error => {
console.error('Ошибка при загрузке схемы покрытия:', error);
alert(error.message);
});
}
function getCoverageSchemeRequestOptions() {
const parseOptionalNumber = (elementId) => {
const input = document.getElementById(elementId);
if (!input) {
return null;
}
const rawValue = String(input.value ?? '').trim();
if (rawValue === '') {
return null;
}
const parsedValue = Number.parseFloat(rawValue);
return Number.isFinite(parsedValue) ? parsedValue : null;
};
return {
rollStepDegrees: parseOptionalNumber('coverageSchemeRollStep'),
minimumTechnologyOverlap: parseOptionalNumber('coverageSchemeMinOverlap'),
orientationToleranceDegrees: parseOptionalNumber('coverageSchemeOrientationTolerance'),
strictContinuousCoverage: document.getElementById('coverageSchemeStrictContinuous')?.checked ?? true,
allowPartialCoverage: document.getElementById('coverageSchemeAllowPartial')?.checked ?? false,
includeDebugInfo: document.getElementById('coverageSchemeIncludeDebug')?.checked ?? true
};
}
// Обработка и отображение слотов
function processAndDisplaySlots(slots, request) {
currentSlotsData = normalizeRequestSlots(slots);
currentRequestId = request.requestId;
setSlotsContext({
type: 'request',
key: request.requestId,
dataSourceName: 'req_cov_' + request.requestId
});
// Обновляем заголовок
document.getElementById('slotsTitle').textContent =
`Интервалы покрытия заявки "${request.name}" (${currentSlotsData.length} интервалов)`;
updateSlotsTableHeaders();
// Показываем контейнер слотов
document.getElementById('slotsContainer').style.display = 'block';
// Рендерим таблицу
renderSlotsTable();
// Обновляем сводную информацию
updateSlotsSummary();
}
function processAndDisplayCoverageSchemeModes(modes, request) {
currentSlotsData = normalizeCoverageSchemeModes(modes);
currentRequestId = null;
setSlotsContext({
type: 'coverage-scheme',
key: request.requestId,
dataSourceName: getCoverageSchemeDataSourceName(request.requestId)
});
document.getElementById('slotsTitle').textContent =
`Схема покрытия заявки "${request.name}" (${currentSlotsData.length} режимов)`;
document.getElementById('slotsContainer').style.display = 'block';
updateSlotsTableHeaders();
renderSlotsTable();
updateSlotsSummary();
}
function updateSlotsTableHeaders() {
const primaryHeader = document.getElementById('slotsPrimaryHeader');
const stateHeader = document.getElementById('slotsStateHeader');
const isCoverageScheme = currentSlotsContext && currentSlotsContext.type === 'coverage-scheme';
if (primaryHeader) {
primaryHeader.childNodes[0].textContent = isCoverageScheme ? '№ режима' : '№ слота';
}
if (stateHeader) {
stateHeader.childNodes[0].textContent = isCoverageScheme ? '' : 'Статус';
}
}
// Рендеринг таблицы слотов
function renderSlotsTable() {
const tbody = document.getElementById('slotsTableBody');
if (!tbody) return;
// Очищаем таблицу
tbody.innerHTML = '';
if (currentSlotsData.length === 0) {
tbody.innerHTML = `