This commit is contained in:
Дмитрий Соловьев
2026-05-25 14:23:52 +03:00
parent b3a6012ebb
commit d48ddd2657
1066 changed files with 104601 additions and 3 deletions
@@ -0,0 +1,156 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>Ком.План</title>
<link rel="stylesheet" href="/css/complex-plan.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div class="complex-plan-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">Ком.План</h2>
<div class="complex-plan-helper">История расчетов комплексного плана и параметры выбранного запуска.</div>
</div>
<button id="complex-plan-refresh" type="button" class="btn btn-outline-primary btn-sm">Обновить</button>
</div>
<div id="complex-plan-alert"></div>
<div class="row g-4">
<div class="col-12 col-xl-5">
<div class="complex-plan-panel">
<div class="complex-plan-panel-header">
<div>
<div class="complex-plan-section-title">Расчеты</div>
<div id="complex-plan-list-meta" class="complex-plan-helper mt-1">Загрузка...</div>
</div>
</div>
<div class="complex-plan-table-wrap">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th>ID</th>
<th>Статус</th>
<th>Интервал</th>
<th>КА</th>
<th></th>
</tr>
</thead>
<tbody id="complex-plan-runs-body">
<tr>
<td colspan="5" class="text-center text-muted py-5">Загрузка...</td>
</tr>
</tbody>
</table>
</div>
<div id="complex-plan-pagination" class="complex-plan-pagination"></div>
</div>
<div class="complex-plan-panel mt-3">
<div class="complex-plan-panel-header">
<div>
<div class="complex-plan-section-title">Запуск расчета</div>
<div class="complex-plan-helper mt-1">Создание новой версии комплексного плана.</div>
</div>
</div>
<form id="complex-plan-process-form" class="complex-plan-form">
<div class="mb-3">
<label for="complex-plan-interval-start" class="form-label">Начало интервала</label>
<input id="complex-plan-interval-start" type="datetime-local" class="form-control form-control-sm" required>
</div>
<div class="mb-3">
<label for="complex-plan-interval-end" class="form-label">Конец интервала</label>
<input id="complex-plan-interval-end" type="datetime-local" class="form-control form-control-sm" required>
</div>
<div class="mb-3">
<label for="complex-plan-satellite-ids" class="form-label">Satellite IDs</label>
<input id="complex-plan-satellite-ids"
type="text"
class="form-control form-control-sm"
required
placeholder="101, 202, 303">
</div>
<div class="row g-2">
<div class="col-12 col-md-6">
<label for="complex-plan-sun" class="form-label">Солнце</label>
<input id="complex-plan-sun"
type="number"
class="form-control form-control-sm"
min="-90"
max="90"
step="0.1"
placeholder="null">
</div>
<div class="col-12 col-md-6">
<label for="complex-plan-coverage-source" class="form-label">Источник покрытия</label>
<select id="complex-plan-coverage-source" class="form-select form-select-sm">
<option value="SLOTS" selected>SLOTS</option>
<option value="COVERAGE_SCHEME">COVERAGE_SCHEME</option>
</select>
</div>
</div>
<div class="row g-2 mt-1">
<div class="col-12 col-md-6">
<label for="complex-plan-coverage-strategy" class="form-label">Вариант покрытия слотами</label>
<select id="complex-plan-coverage-strategy" class="form-select form-select-sm">
<option value="GREEDY" selected>GREEDY</option>
<option value="CONTINUOUS">CONTINUOUS</option>
</select>
</div>
<div class="col-12 col-md-6">
<label for="complex-plan-count-lat" class="form-label">Объединение по широте</label>
<input id="complex-plan-count-lat"
type="number"
class="form-control form-control-sm"
min="1"
step="1"
placeholder="countLat">
</div>
</div>
<div class="row g-2 mt-1">
<div class="col-12 col-md-6">
<label for="complex-plan-count-long" class="form-label">Объединение по долготе</label>
<input id="complex-plan-count-long"
type="number"
class="form-control form-control-sm"
min="1"
step="1"
placeholder="countLong">
</div>
</div>
<button id="complex-plan-process-submit" type="submit" class="btn btn-primary btn-sm mt-3">
Запустить
</button>
</form>
</div>
</div>
<div class="col-12 col-xl-7">
<div class="complex-plan-panel">
<div class="complex-plan-panel-header">
<div>
<div class="complex-plan-section-title">Детали расчета</div>
<div id="complex-plan-detail-meta" class="complex-plan-helper mt-1">Выберите расчет слева.</div>
</div>
<button id="complex-plan-report"
type="button"
class="btn btn-outline-primary btn-sm"
disabled>
CSV
</button>
</div>
<div id="complex-plan-detail" class="complex-plan-detail">
<div class="text-muted py-5 text-center">Нет выбранного расчета</div>
</div>
</div>
</div>
</div>
</div>
<script src="/complex_plan_scripts.js"></script>
</th:block>
</body>
</html>
@@ -0,0 +1,164 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>Текущие планы</title>
<link rel="stylesheet" href="/css/catalog.css"/>
<link rel="stylesheet" href="/css/current-plans.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div id="current-plans-page" class="catalog-page current-plans-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">Текущие планы</h2>
<div class="catalog-helper">Просмотр миссий выбранного спутника и режимов работы внутри миссии.</div>
</div>
<div class="d-flex gap-2 flex-wrap">
<button id="current-plans-create-mission" type="button" class="btn btn-primary" disabled>Создать план</button>
<button id="current-plans-refresh" type="button" class="btn btn-outline-secondary">Обновить</button>
</div>
</div>
<div id="current-plans-alert"></div>
<div class="row g-4 current-plans-layout">
<div class="col-12 col-xl-4">
<div class="card catalog-card current-plans-sidebar h-100">
<div class="card-header">
<div class="catalog-section-title">Спутники</div>
<div class="catalog-helper mt-1">Выберите КА для просмотра текущих планов.</div>
</div>
<div class="card-body p-3">
<label for="current-plans-satellite-filter" class="form-label">Фильтр</label>
<input id="current-plans-satellite-filter"
type="search"
class="form-control form-control-sm"
autocomplete="off"
placeholder="ID, NORAD, код, имя, тип">
</div>
<div class="card-body catalog-list p-0">
<table class="table table-hover mb-0 current-plans-satellites-table">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Спутник</th>
<th>Тип</th>
</tr>
</thead>
<tbody id="current-plans-satellites-body"></tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-xl-8">
<div class="d-flex flex-column gap-4 h-100 current-plans-main-column">
<div class="card catalog-card current-plans-missions-card">
<div class="card-header d-flex justify-content-between align-items-start gap-3 flex-wrap">
<div>
<div class="catalog-section-title">Миссии выбранного спутника</div>
<div id="current-plans-selected-satellite" class="catalog-helper mt-1">Выберите спутник слева.</div>
</div>
<div class="current-plans-pagination">
<button id="current-plans-missions-prev" type="button" class="btn btn-outline-secondary btn-sm" disabled>Назад</button>
<span id="current-plans-missions-page" class="catalog-helper mx-2">0 / 0</span>
<button id="current-plans-missions-next" type="button" class="btn btn-outline-secondary btn-sm" disabled>Вперёд</button>
</div>
</div>
<div class="card-body p-0">
<div id="current-plans-missions-empty" class="catalog-empty">Выберите спутник для загрузки миссий.</div>
<div class="table-responsive current-plans-table-wrap d-none" id="current-plans-missions-wrap">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Миссия</th>
<th>Начало</th>
<th>Конец</th>
<th>Станция</th>
<th>Статус</th>
<th class="current-plans-actions-col">Действия</th>
</tr>
</thead>
<tbody id="current-plans-missions-body"></tbody>
</table>
</div>
</div>
</div>
<div class="card catalog-card current-plans-modes-card flex-grow-1">
<div class="card-header d-flex justify-content-between align-items-start gap-3 flex-wrap">
<div>
<div class="catalog-section-title">Режимы работы внутри миссии</div>
<div id="current-plans-selected-mission" class="catalog-helper mt-1">Выберите миссию в верхней таблице.</div>
</div>
<button id="current-plans-export-csv" type="button" class="btn btn-outline-secondary btn-sm" disabled>Сохранить CSV</button>
</div>
<div class="card-body p-0">
<div id="current-plans-modes-empty" class="catalog-empty">Режимы появятся после выбора миссии.</div>
<div class="table-responsive current-plans-table-wrap d-none" id="current-plans-modes-wrap">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Тип</th>
<th>Начало</th>
<th>Виток</th>
<th>Длительность, с</th>
<th>Параметры</th>
<th>Статус / источник</th>
</tr>
</thead>
<tbody id="current-plans-modes-body"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="current-plans-create-modal" tabindex="-1" aria-labelledby="current-plans-create-modal-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<form id="current-plans-create-form" class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="current-plans-create-modal-title">Создание текущего плана</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Спутник</label>
<input id="current-plans-create-satellite" type="text" class="form-control" readonly>
</div>
<div class="row g-3">
<div class="col-12 col-md-6">
<label for="current-plans-create-start" class="form-label">Начало миссии</label>
<input id="current-plans-create-start" type="datetime-local" class="form-control" required>
</div>
<div class="col-12 col-md-6">
<label for="current-plans-create-stop" class="form-label">Конец миссии</label>
<input id="current-plans-create-stop" type="datetime-local" class="form-control" required>
</div>
</div>
<div class="mt-3">
<label for="current-plans-create-station" class="form-label">Станция</label>
<input id="current-plans-create-station" type="text" class="form-control" maxlength="32" placeholder="Необязательно">
</div>
<div class="mt-3">
<label for="current-plans-create-status" class="form-label">Статус</label>
<input id="current-plans-create-status" type="text" class="form-control" maxlength="15" value="NEW">
</div>
<div class="catalog-helper mt-3">План будет создан для выбранного спутника. Расчёт съёмки и сбросов запускается кнопками в строке плана.</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Отмена</button>
<button id="current-plans-create-submit" type="submit" class="btn btn-primary">Создать</button>
</div>
</form>
</div>
</div>
<script src="/current_plans_scripts.js"></script>
</th:block>
</body>
</html>
@@ -0,0 +1,171 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>Dynamic Plan</title>
<link rel="stylesheet" href="/css/dynamic-plan.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div class="dynamic-plan-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">Dynamic Plan</h2>
<div class="dynamic-plan-helper">Запуск расчета модели комплексного плана по одной заявке.</div>
</div>
</div>
<div id="dynamic-plan-alert"></div>
<div class="row g-4">
<div class="col-12 col-xl-5">
<div class="dynamic-plan-panel">
<div class="dynamic-plan-panel-header">
<div>
<div class="dynamic-plan-section-title">Параметры расчета</div>
<div class="dynamic-plan-helper mt-1">UUID заявки, выбранные КА и расчетный интервал.</div>
</div>
</div>
<form id="dynamic-plan-form" class="dynamic-plan-form">
<div class="mb-3">
<label for="dynamic-plan-request-id" class="form-label">UUID заявки</label>
<input id="dynamic-plan-request-id"
type="text"
class="form-control form-control-sm"
required
placeholder="00000000-0000-0000-0000-000000000000">
</div>
<div class="mb-3">
<label for="dynamic-plan-satellite-ids" class="form-label">Идентификаторы КА</label>
<input id="dynamic-plan-satellite-ids"
type="text"
class="form-control form-control-sm"
required
placeholder="56756, 62138">
</div>
<div class="mb-3">
<label class="form-label">Вариант расчета</label>
<div class="dynamic-plan-mode-group" role="group" aria-label="Вариант расчета">
<input id="dynamic-plan-mode-full"
type="radio"
class="btn-check"
name="dynamic-plan-mode"
value="FULL"
autocomplete="off"
checked>
<label class="btn btn-outline-primary btn-sm" for="dynamic-plan-mode-full">
Полный
</label>
<input id="dynamic-plan-mode-greedy"
type="radio"
class="btn-check"
name="dynamic-plan-mode"
value="GREEDY"
autocomplete="off">
<label class="btn btn-outline-primary btn-sm" for="dynamic-plan-mode-greedy">
Жадный
</label>
</div>
</div>
<div class="mb-3">
<label class="form-label">Виток для жадного расчета</label>
<div class="dynamic-plan-revolution-group" role="group" aria-label="Виток для жадного расчета">
<input id="dynamic-plan-revolution-both"
type="radio"
class="btn-check"
name="dynamic-plan-revolution-mode"
value="BOTH"
autocomplete="off"
checked>
<label class="btn btn-outline-primary btn-sm" for="dynamic-plan-revolution-both">
Оба
</label>
<input id="dynamic-plan-revolution-asc"
type="radio"
class="btn-check"
name="dynamic-plan-revolution-mode"
value="ASC"
autocomplete="off">
<label class="btn btn-outline-primary btn-sm" for="dynamic-plan-revolution-asc">
Восх.
</label>
<input id="dynamic-plan-revolution-desc"
type="radio"
class="btn-check"
name="dynamic-plan-revolution-mode"
value="DESC"
autocomplete="off">
<label class="btn btn-outline-primary btn-sm" for="dynamic-plan-revolution-desc">
Нисх.
</label>
</div>
</div>
<div class="form-check mb-3">
<input id="dynamic-plan-filter-covered-routes"
type="checkbox"
class="form-check-input">
<label for="dynamic-plan-filter-covered-routes" class="form-check-label">
Удалять маршруты по уже снятой территории
</label>
</div>
<div class="mb-3">
<label for="dynamic-plan-interval-start" class="form-label">Дата начала интервала</label>
<input id="dynamic-plan-interval-start"
type="date"
class="form-control form-control-sm"
required>
</div>
<div class="mb-3">
<label for="dynamic-plan-interval-end" class="form-label">Дата конца интервала</label>
<input id="dynamic-plan-interval-end"
type="date"
class="form-control form-control-sm"
required>
</div>
<button id="dynamic-plan-submit" type="submit" class="btn btn-primary btn-sm">
Запустить
</button>
</form>
</div>
<div class="dynamic-plan-panel mt-4">
<div class="dynamic-plan-panel-header">
<div>
<div class="dynamic-plan-section-title">История запусков</div>
<div id="dynamic-plan-runs-meta" class="dynamic-plan-helper mt-1">Последние расчеты.</div>
</div>
<button id="dynamic-plan-runs-refresh" type="button" class="btn btn-outline-secondary btn-sm">
Обновить
</button>
</div>
<div id="dynamic-plan-runs-body" class="dynamic-plan-runs">
<div class="text-muted py-4 text-center">Нет данных</div>
</div>
</div>
</div>
<div class="col-12 col-xl-7">
<div class="dynamic-plan-panel">
<div class="dynamic-plan-panel-header">
<div>
<div class="dynamic-plan-section-title">Запуск и результат</div>
<div id="dynamic-plan-result-meta" class="dynamic-plan-helper mt-1">Расчет еще не запускался.</div>
</div>
</div>
<div id="dynamic-plan-result" class="dynamic-plan-result">
<div class="text-muted py-5 text-center">Нет данных</div>
</div>
</div>
</div>
</div>
</div>
<script src="/dynamic_plan_map_layers.js"></script>
<script src="/dynamic_plan_scripts.js"></script>
</th:block>
</body>
</html>
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org">
<head th:fragment="head">
<meta charset="UTF-8">
<link rel="stylesheet"
href="/webjars/bootstrap/5.3.0/css/bootstrap.min.css"/>
</head>
<body>
</body>
<footer th:fragment="footer">
<script src="/webjars/jquery/3.6.2/jquery.min.js"></script>
<script src="/webjars/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
</footer>
</html>
@@ -0,0 +1,78 @@
<ul xmlns:th="http://www.thymeleaf.org"
th:fragment="menu-generator(elements)"
class="navbar-nav ml-auto">
<li
th:each="element: ${elements}"
class="nav-item">
<a class="nav-link"
th:href="${element[1]}">[[${element[0]}]]</a>
</li>
</ul>
<nav xmlns:th="http://www.thymeleaf.org"
th:fragment="menu"
class="navbar navbar-expand-lg static-top" style="background-color: #e3f2fd;">
<div class="container-fluid">
<a class="navbar-brand"
href="/">ПЦП</a>
<button class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarResponsive"
aria-controls="navbarResponsive"
aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false">Группировки</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/groups">Управление</a></li>
<li><a class="dropdown-item" href="/group-state">Состояние ОГ</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false">Спутники</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/satellites">Управление</a></li>
<li><a class="dropdown-item" href="/satellites/pdcm">ПДЦМ</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false">Станции</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/stations">Управление</a></li>
<li><a class="dropdown-item" href="/rva">ЗРВ</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="/map">Карта</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/com-plan">Ком.План</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/dynamic-plan">Dynamic Plan</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/current-plans">Текущие планы</a>
</li>
</ul>
</div>
</div>
</nav>
@@ -0,0 +1,90 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>Состояние ОГ</title>
<link rel="stylesheet" href="/css/catalog.css"/>
<link rel="stylesheet" href="/css/group-state.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div id="group-state-page" class="catalog-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">Состояние ОГ</h2>
<div class="catalog-helper">Состояние орбитальной группировки на выбранный момент времени.</div>
</div>
<button id="group-state-refresh" type="button" class="btn btn-outline-secondary">Обновить</button>
</div>
<div id="group-state-alert"></div>
<div class="row g-4">
<div class="col-12 col-xl-4">
<div class="card catalog-card group-state-sidebar h-100">
<div class="card-header">
<div class="catalog-section-title">Доступные группировки</div>
</div>
<div class="card-body catalog-list p-0">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>ОГ</th>
<th>КА</th>
<th>Интервал</th>
</tr>
</thead>
<tbody id="group-state-list"></tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-xl-8">
<div class="d-flex flex-column gap-4">
<div class="card catalog-card">
<div class="card-header">
<div class="catalog-section-title">Время</div>
</div>
<div class="card-body">
<div id="group-state-summary" class="catalog-helper mb-3">Выберите группировку слева.</div>
<div class="group-state-time-grid">
<div>
<label for="group-state-time" class="form-label">Момент времени</label>
<input id="group-state-time" type="datetime-local" step="1" class="form-control"/>
</div>
<div class="d-flex align-items-end">
<button id="group-state-apply" type="button" class="btn btn-primary w-100">Показать состояние</button>
</div>
</div>
</div>
</div>
<div class="card catalog-card">
<div class="card-header">
<div class="catalog-section-title">Таблица</div>
</div>
<div class="card-body">
<div id="group-state-table-empty" class="catalog-empty">Выберите группировку с доступным интервалом расчета.</div>
<div id="group-state-table-wrap" class="group-state-table-wrap d-none"></div>
</div>
</div>
<div class="card catalog-card">
<div class="card-header">
<div class="catalog-section-title">График</div>
</div>
<div class="card-body">
<div id="group-state-charts-empty" class="catalog-empty">Графики появятся после выбора момента времени.</div>
<div id="group-state-charts" class="group-state-chart-grid d-none"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/group_state_scripts.js"></script>
</th:block>
</body>
</html>
@@ -0,0 +1,85 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>Группировки</title>
<link rel="stylesheet" href="/css/catalog.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div id="catalog-page" data-page="groups" class="catalog-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">Группировки</h2>
<div class="catalog-helper">Орбитальные группировки и управление их составом.</div>
</div>
<div class="d-flex gap-2">
<button id="groups-refresh" type="button" class="btn btn-outline-secondary">Обновить</button>
<button id="groups-new" type="button" class="btn btn-primary">Новая группировка</button>
</div>
</div>
<div id="groups-alert"></div>
<div class="row g-4">
<div class="col-12 col-xl-5">
<div class="card catalog-card h-100">
<div class="card-header">
<div class="catalog-section-title">Список группировок</div>
</div>
<div class="card-body catalog-list p-0">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Название</th>
<th>Состав</th>
<th>Спутники</th>
</tr>
</thead>
<tbody id="groups-table-body"></tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-xl-7">
<div class="card catalog-card h-100">
<div class="card-header">
<div class="catalog-section-title" id="group-form-title">Новая группировка</div>
</div>
<div class="card-body">
<form id="group-form" class="d-flex flex-column gap-3">
<input id="group-id" type="hidden"/>
<div>
<label for="group-name" class="form-label">Название</label>
<input id="group-name" class="form-control" required/>
</div>
<div>
<div class="d-flex justify-content-between align-items-center mb-2">
<label class="form-label mb-0">Состав группировки</label>
<span class="catalog-helper">Можно выбрать несколько спутников</span>
</div>
<div id="group-satellite-checklist" class="catalog-checkbox-list"></div>
</div>
<div class="d-flex gap-2 flex-wrap">
<button type="submit" class="btn btn-primary">Сохранить</button>
<button id="groups-delete" type="button" class="btn btn-outline-danger">Удалить</button>
<button id="groups-reset" type="reset" class="btn btn-outline-secondary">Сбросить</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="/catalog_scripts.js"></script>
<script>
document.getElementById('groups-reset').addEventListener('click', function () {
document.getElementById('groups-new').click();
});
</script>
</th:block>
</body>
</html>
@@ -0,0 +1,12 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>ПЦП</title>
</head>
<body>
<th:block layout:fragment="content">
</th:block>
</body>
</html>
@@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml">
<head>
<th:block th:insert="~{fragments/base/general.html :: head}" />
</head>
<body>
<div class="vh-100">
<div style="width:100%; position:fixed; z-index:1;">
<div th:replace="~{fragments/base/menu.html :: menu}"></div>
</div>
<div class="container-fluid d-flex flex-column"
style="padding-top:4rem; height:calc(100% - 1rem);">
<div layout:fragment="content">
</div>
</div>
</div>
</body>
<th:block th:insert="~{fragments/base/general.html :: footer}"/>
</html>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,127 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>ЗРВ</title>
<link rel="stylesheet" href="/css/rva.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div class="rva-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">ЗРВ</h2>
<div class="rva-helper">Расчет зон радиовидимости по нескольким станциям с объединением интервалов или без него.</div>
</div>
</div>
<div id="rva-alert"></div>
<div class="row g-4">
<div class="col-12 col-xxl-4">
<div class="card rva-card">
<div class="card-header">
<div class="rva-section-title">Параметры расчета</div>
</div>
<div class="card-body">
<form id="rva-form" class="d-flex flex-column gap-3">
<div>
<label for="rva-satellite" class="form-label">Спутник</label>
<select id="rva-satellite" class="form-select" required>
<option value="">Выберите спутник</option>
<option th:each="satellite : ${satellites}"
th:value="${satellite.noradId}"
th:text="${satellite.name + ' (' + satellite.noradId + ')'}"></option>
</select>
</div>
<div>
<label for="rva-duration" class="form-label">Горизонт расчета, суток</label>
<input id="rva-duration" type="number" min="1" step="1" value="1" class="form-control" required/>
</div>
<div>
<div class="form-label">Вариант расчета</div>
<div class="d-flex flex-column gap-2">
<div class="form-check">
<input class="form-check-input" type="radio" name="rva-mode" id="rva-mode-common" value="common" checked>
<label class="form-check-label" for="rva-mode-common">Полный список ЗРВ</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="rva-mode" id="rva-mode-merge" value="merge">
<label class="form-check-label" for="rva-mode-merge">Объединенные интервалы</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="rva-mode" id="rva-mode-merge-on-rev" value="merge-on-rev">
<label class="form-check-label" for="rva-mode-merge-on-rev">Суммарная длительность на витке</label>
</div>
</div>
</div>
<div>
<div class="d-flex justify-content-between align-items-center mb-2">
<label class="form-label mb-0">Станции</label>
<button id="rva-select-all" type="button" class="btn btn-outline-secondary btn-sm">Выбрать все</button>
</div>
<div id="rva-stations" class="rva-stations">
<label class="form-check rva-station-item"
th:each="station : ${stations}">
<input class="form-check-input rva-station-checkbox"
type="checkbox"
th:value="${station.number}"
th:attr="data-number=${station.number}">
<span class="form-check-label rva-station-label">
<span class="fw-semibold" th:text="${station.name}"></span>
<span class="text-muted" th:text="${' (#' + station.number + ')'}"></span>
</span>
</label>
</div>
<div class="rva-helper mt-2">Нужно выбрать хотя бы одну станцию.</div>
</div>
<div class="d-flex gap-2 flex-wrap">
<button type="submit" class="btn btn-primary">Рассчитать</button>
<button id="rva-reset" type="reset" class="btn btn-outline-secondary">Сбросить</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-12 col-xxl-8">
<div class="card rva-card">
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
<div>
<div class="rva-section-title">Результаты</div>
<div id="rva-result-meta" class="rva-helper mt-1">Выберите параметры и запустите расчет.</div>
</div>
<button id="rva-download" type="button" class="btn btn-outline-primary btn-sm" disabled>Скачать CSV</button>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead id="rva-results-head"></thead>
<tbody id="rva-results-body">
<tr>
<td class="text-center text-muted py-5">Нет результатов</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script th:inline="javascript">
window.rvaPageData = {
satellites: /*[[${satellites}]]*/ [],
stations: /*[[${stations}]]*/ []
};
</script>
<script src="/rva_scripts.js"></script>
</th:block>
</body>
</html>
@@ -0,0 +1,73 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>ПДЦМ</title>
<link rel="stylesheet" href="/css/catalog.css"/>
<link rel="stylesheet" href="/css/satellite-pdcm.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div id="satellite-pdcm-page" class="catalog-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">Параметры движения центра масс</h2>
<div class="catalog-helper">Табличное представление asc-node по выбранному спутнику.</div>
</div>
<button id="satellite-pdcm-refresh" type="button" class="btn btn-outline-secondary">Обновить</button>
</div>
<div id="satellite-pdcm-alert"></div>
<div class="row g-4">
<div class="col-12 col-lg-4 col-xl-3">
<div class="card catalog-card catalog-list-card satellite-pdcm-sidebar h-100">
<div class="card-header">
<div class="catalog-section-title">Спутники</div>
</div>
<div class="card-body catalog-list p-0">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Спутник</th>
<th>Интервал</th>
</tr>
</thead>
<tbody id="satellite-pdcm-list"></tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-lg-8 col-xl-9">
<div class="d-flex flex-column gap-4">
<div class="card catalog-card">
<div class="card-header">
<div class="catalog-section-title">Сводка</div>
</div>
<div class="card-body">
<div id="satellite-pdcm-summary" class="satellite-pdcm-summary-root">
<div class="catalog-helper">Выберите спутник слева.</div>
</div>
</div>
</div>
<div class="card catalog-card">
<div class="card-header d-flex justify-content-between align-items-center gap-2 flex-wrap">
<div class="catalog-section-title">Asc-node</div>
<button id="satellite-pdcm-export" type="button" class="btn btn-sm btn-outline-primary" disabled>Скачать CSV</button>
</div>
<div class="card-body">
<div id="satellite-pdcm-table-empty" class="catalog-empty">Нет доступного интервала ПДЦМ для выбранного спутника.</div>
<div id="satellite-pdcm-table-wrap" class="satellite-pdcm-table-wrap d-none"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/satellite_pdcm_scripts.js"></script>
</th:block>
</body>
</html>
@@ -0,0 +1,273 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>Спутники</title>
<link rel="stylesheet" href="/css/catalog.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div id="catalog-page" data-page="satellites" class="catalog-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">Спутники</h2>
<div class="catalog-helper">Карточки спутников, observation profile и slot profile.</div>
</div>
<div class="d-flex gap-2">
<button id="satellites-refresh" type="button" class="btn btn-outline-secondary">Обновить</button>
<button id="satellites-new" type="button" class="btn btn-primary">Новый спутник</button>
</div>
</div>
<div id="satellites-alert"></div>
<div class="row g-4">
<div class="col-12 col-xxl-4">
<div class="card catalog-card catalog-list-card h-100">
<div class="card-header">
<div class="catalog-section-title">Список спутников</div>
</div>
<div class="card-body catalog-list-search p-3 border-bottom">
<input id="satellite-search" class="form-control" placeholder="Поиск по ID, имени, коду"/>
</div>
<div class="card-body catalog-list catalog-list-page-scroll p-0">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Код</th>
<th>Имя</th>
<th>Тип</th>
<th>Слоты</th>
</tr>
</thead>
<tbody id="satellites-table-body"></tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-xxl-8">
<div class="d-flex flex-column gap-4">
<div class="card catalog-card">
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
<div class="catalog-section-title" id="satellite-form-title">Новый спутник</div>
<button id="satellite-delete" type="button" class="btn btn-outline-danger btn-sm">Удалить спутник</button>
</div>
<div class="card-body">
<form id="satellite-form" class="d-flex flex-column gap-3">
<div class="catalog-form-grid">
<div>
<label for="satellite-id" class="form-label">ID</label>
<input id="satellite-id" type="number" min="1" class="form-control" required/>
</div>
<div>
<label for="satellite-norad-id" class="form-label">NORAD ID</label>
<input id="satellite-norad-id" type="number" min="1" class="form-control"/>
</div>
<div>
<label for="satellite-code" class="form-label">Code</label>
<input id="satellite-code" class="form-control" required/>
</div>
<div>
<label for="satellite-name" class="form-label">Name</label>
<input id="satellite-name" class="form-control" required/>
</div>
<div>
<label for="satellite-type-code" class="form-label">Type code</label>
<input id="satellite-type-code" class="form-control" required/>
</div>
</div>
<div class="catalog-form-grid align-items-end">
<div class="form-check">
<input id="satellite-active" class="form-check-input" type="checkbox" checked>
<label class="form-check-label" for="satellite-active">Активен</label>
</div>
<div class="form-check">
<input id="satellite-scan-tle" class="form-check-input" type="checkbox">
<label class="form-check-label" for="satellite-scan-tle">Сканировать TLE</label>
</div>
<div>
<label for="satellite-red" class="form-label">Red</label>
<input id="satellite-red" type="number" min="0" max="255" class="form-control" value="255" required/>
</div>
<div>
<label for="satellite-green" class="form-label">Green</label>
<input id="satellite-green" type="number" min="0" max="255" class="form-control" value="0" required/>
</div>
<div>
<label for="satellite-blue" class="form-label">Blue</label>
<input id="satellite-blue" type="number" min="0" max="255" class="form-control" value="0" required/>
</div>
<div>
<label class="form-label">Preview</label>
<div id="satellite-color-preview" class="catalog-color-preview"></div>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Сохранить карточку</button>
</div>
</form>
</div>
</div>
<div class="card catalog-card">
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
<div class="catalog-section-title">Observation profile</div>
<button id="observation-delete" type="button" class="btn btn-outline-danger btn-sm">Удалить profile</button>
</div>
<div class="card-body">
<form id="observation-form" class="d-flex flex-column gap-3">
<div class="catalog-form-grid">
<div>
<label for="observation-capture-angle" class="form-label">Capture angle</label>
<input id="observation-capture-angle" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="observation-sun-angle-min" class="form-label">Sun angle min</label>
<input id="observation-sun-angle-min" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="observation-duration-min" class="form-label">Duration min, s</label>
<input id="observation-duration-min" type="number" min="0" class="form-control" required/>
</div>
<div>
<label for="observation-duration-max" class="form-label">Duration max, s</label>
<input id="observation-duration-max" type="number" min="0" class="form-control" required/>
</div>
<div>
<label for="observation-mmi" class="form-label">MMI, s</label>
<input id="observation-mmi" type="number" min="0" class="form-control" required/>
</div>
<div>
<label for="observation-daily-max" class="form-label">Daily max, s</label>
<input id="observation-daily-max" type="number" min="0" class="form-control" required/>
</div>
<div>
<label for="observation-revolution-max" class="form-label">Revolution max, s</label>
<input id="observation-revolution-max" type="number" min="0" class="form-control" required/>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Сохранить observation profile</button>
</div>
</form>
</div>
</div>
<div class="card catalog-card">
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
<div class="catalog-section-title">Slot profile</div>
<button id="slot-delete" type="button" class="btn btn-outline-danger btn-sm">Удалить profile</button>
</div>
<div class="card-body">
<form id="slot-ic-form" class="d-flex flex-column gap-3">
<div>
<div class="catalog-section-title">Начальные условия орбиты</div>
<div id="slot-ic-status" class="catalog-helper mt-2">Начальные условия не заданы.</div>
</div>
<div class="catalog-form-grid">
<div>
<label for="slot-ic-time" class="form-label">Time</label>
<input id="slot-ic-time" type="text" class="form-control" placeholder="dd.mm.yyyy hh:mm:ss.zzz" required/>
</div>
<div>
<label for="slot-ic-revolution" class="form-label">Revolution</label>
<input id="slot-ic-revolution" type="number" class="form-control" required/>
</div>
<div>
<label for="slot-ic-s-ball" class="form-label">S-ball</label>
<input id="slot-ic-s-ball" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="slot-ic-f81" class="form-label">F81</label>
<input id="slot-ic-f81" type="number" step="any" class="form-control" required/>
</div>
</div>
<div class="catalog-form-grid">
<div>
<label for="slot-ic-x" class="form-label">X</label>
<input id="slot-ic-x" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="slot-ic-y" class="form-label">Y</label>
<input id="slot-ic-y" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="slot-ic-z" class="form-label">Z</label>
<input id="slot-ic-z" type="number" step="any" class="form-control" required/>
</div>
</div>
<div class="catalog-form-grid">
<div>
<label for="slot-ic-vx" class="form-label">Vx</label>
<input id="slot-ic-vx" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="slot-ic-vy" class="form-label">Vy</label>
<input id="slot-ic-vy" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="slot-ic-vz" class="form-label">Vz</label>
<input id="slot-ic-vz" type="number" step="any" class="form-control" required/>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Сохранить начальные условия</button>
</div>
</form>
<hr class="my-4">
<form id="slot-form" class="d-flex flex-column gap-3">
<div class="catalog-form-grid">
<div>
<label for="slot-cycle-revs" class="form-label">Cycle revs</label>
<input id="slot-cycle-revs" type="number" min="0" class="form-control" required/>
</div>
<div>
<label for="slot-tn-calc" class="form-label">TN calc</label>
<input id="slot-tn-calc" type="text" class="form-control" placeholder="dd.mm.yyyy hh:mm:ss.zzz"/>
</div>
<div>
<label for="slot-duration" class="form-label">Slot duration</label>
<input id="slot-duration" type="number" min="0" class="form-control" required/>
</div>
<div>
<label for="slot-duration-calc-days" class="form-label">Duration calc, days</label>
<input id="slot-duration-calc-days" type="number" min="0" class="form-control" required/>
</div>
<div>
<label for="slot-max-chain-length" class="form-label">Max chain length</label>
<input id="slot-max-chain-length" type="number" min="0" class="form-control" required/>
</div>
</div>
<div>
<label for="slot-default-angles" class="form-label">Default angles</label>
<textarea id="slot-default-angles" class="form-control" rows="8" placeholder="18.5, 21.58&#10;20.85, 23.82"></textarea>
<div class="catalog-helper mt-2">Один диапазон на строку: `angleBegin, angleEnd`.</div>
</div>
<div class="d-flex flex-wrap gap-2">
<button id="slot-calculate" type="button" class="btn btn-outline-secondary">Расчет слотов</button>
<button type="submit" class="btn btn-primary">Сохранить slot profile</button>
</div>
</form>
<div id="slot-calculation-summary" class="catalog-slot-summary mt-4"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/catalog_scripts.js"></script>
</th:block>
</body>
</html>
@@ -0,0 +1,110 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layouts/main-layout}">
<head>
<title>Станции</title>
<link rel="stylesheet" href="/css/catalog.css"/>
</head>
<body>
<th:block layout:fragment="content">
<div id="stations-page" class="catalog-page py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<h2 class="mb-1">Станции</h2>
<div class="catalog-helper">CRUD-операции над наземными станциями через `stations-service`.</div>
</div>
<div class="d-flex gap-2">
<button id="stations-refresh" type="button" class="btn btn-outline-secondary">Обновить</button>
<button id="stations-new" type="button" class="btn btn-primary">Новая станция</button>
</div>
</div>
<div id="stations-alert"></div>
<div class="row g-4">
<div class="col-12 col-xl-5">
<div class="card catalog-card h-100">
<div class="card-header">
<div class="catalog-section-title">Список станций</div>
</div>
<div class="card-body p-3 border-bottom">
<input id="stations-search" class="form-control" placeholder="Поиск по номеру, имени"/>
</div>
<div class="card-body catalog-list p-0">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Номер</th>
<th>Название</th>
<th>Мин.угол</th>
<th>Макс.угол</th>
</tr>
</thead>
<tbody id="stations-table-body"></tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-xl-7">
<div class="card catalog-card h-100">
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
<div class="catalog-section-title" id="station-form-title">Новая станция</div>
<button id="stations-delete" type="button" class="btn btn-outline-danger btn-sm">Удалить станцию</button>
</div>
<div class="card-body">
<form id="station-form" class="d-flex flex-column gap-3">
<input id="station-id" type="hidden"/>
<div class="catalog-form-grid">
<div>
<label for="station-number" class="form-label">Номер</label>
<input id="station-number" type="number" class="form-control" min="0" required/>
</div>
<div>
<label for="station-name" class="form-label">Название</label>
<input id="station-name" class="form-control" required/>
</div>
</div>
<div class="catalog-form-grid">
<div>
<label for="station-lat" class="form-label">Широта</label>
<input id="station-lat" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="station-long" class="form-label">Долгота</label>
<input id="station-long" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="station-height" class="form-label">Высота, м</label>
<input id="station-height" type="number" step="any" min="0" class="form-control" required/>
</div>
</div>
<div class="catalog-form-grid">
<div>
<label for="station-elevation-min" class="form-label">Мин.угол</label>
<input id="station-elevation-min" type="number" step="any" class="form-control" required/>
</div>
<div>
<label for="station-elevation-max" class="form-label">Макс.угол</label>
<input id="station-elevation-max" type="number" step="any" class="form-control" required/>
</div>
</div>
<div class="d-flex gap-2 flex-wrap">
<button type="submit" class="btn btn-primary">Сохранить</button>
<button id="stations-reset" type="reset" class="btn btn-outline-secondary">Сбросить</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="/stations_scripts.js"></script>
</th:block>
</body>
</html>