Задание НУ из ui

This commit is contained in:
emelianov
2026-05-29 12:41:32 +03:00
parent d693bf799d
commit 3fd702ca47
12 changed files with 418 additions and 12 deletions
@@ -91,6 +91,16 @@ class CatalogRestController(
return ResponseEntity.accepted().build()
}
@GetMapping("/satellites/pdcm/{satelliteId}/initial-conditions")
fun currentPdcmInitialConditions(@PathVariable satelliteId: Long): ResponseEntity<Any> {
val response = ballisticsService.currentInitialConditions(satelliteId)
return if (response == null) {
ResponseEntity.noContent().build()
} else {
ResponseEntity.ok(response)
}
}
private fun parseSendInitialConditionsTime(time: String?): LocalDateTime {
val normalizedTime = time?.trim()?.takeIf { it.isNotEmpty() }
?: return LocalDateTime.now()
@@ -82,6 +82,19 @@ class BallisticsService (webClientBuilderProvider: ObjectProvider<WebClient.Buil
.block()
}
fun currentInitialConditions(satelliteId: Long): SatelliteICDTO? =
webClientBuilder.build()
.get()
.uri("$url/api/satellites/$satelliteId/initial-conditions/current")
.exchangeToMono { response ->
when {
response.statusCode().is2xxSuccessful -> response.bodyToMono(SatelliteICDTO::class.java)
response.statusCode().value() == 404 || response.statusCode().value() == 204 -> reactor.core.publisher.Mono.empty()
else -> response.createException().flatMap { reactor.core.publisher.Mono.error(it) }
}
}
.block()
fun parseTle(tle: TLEDTO): TleParsedDTO =
webClientBuilder.build()
.post()
@@ -68,6 +68,21 @@
return `${pad(now.getDate())}.${pad(now.getMonth() + 1)}.${now.getFullYear()} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}.${pad(now.getMilliseconds(), 3)}`;
}
function formatDateTimeInput(value) {
if (!value) {
return '';
}
const match = String(value).match(
/^(\d{4})-(\d{2})-(\d{2})[T\s](\d{2}):(\d{2})(?::(\d{2})(?:[.,](\d{1,9}))?)?/
);
if (!match) {
return value;
}
const [, year, month, day, hour, minute, second = '00', millis = '000'] = match;
return `${day}.${month}.${year} ${hour}:${minute}:${second}.${millis.slice(0, 3).padEnd(3, '0')}`;
}
function parseSendInitialConditionsTime() {
const input = document.getElementById('satellite-pdcm-send-ic-time');
const value = input?.value.trim() || '';
@@ -194,9 +209,10 @@
function renderSetInitialConditionsState(details, loading = false) {
const openButton = document.getElementById('satellite-pdcm-open-set-ic');
const submitButton = document.getElementById('satellite-pdcm-set-ic-submit');
const satelliteSelected = !!state.selectedSatelliteId;
if (openButton) {
openButton.disabled = !details || loading;
openButton.title = details ? '' : 'Выберите спутник';
openButton.disabled = !satelliteSelected || loading;
openButton.title = satelliteSelected ? '' : 'Выберите спутник';
}
if (submitButton) {
submitButton.disabled = loading;
@@ -429,14 +445,7 @@
}
}
function openSetInitialConditionsModal() {
if (!state.selectedSatelliteId || !state.details) {
showAlert('Выберите спутник для задания начальных условий');
return;
}
document.getElementById('satellite-pdcm-set-ic-target').textContent =
`${state.details.name} · ID ${state.details.satelliteId}`;
function resetInitialConditionsForm() {
document.getElementById('satellite-pdcm-ic-time').value = currentDateTimeInputValue();
document.getElementById('satellite-pdcm-ic-revolution').value = '';
document.getElementById('satellite-pdcm-ic-movement-model').value = 'FOTO';
@@ -445,10 +454,54 @@
['x', 'y', 'z', 'vx', 'vy', 'vz'].forEach(field => {
document.getElementById(`satellite-pdcm-ic-${field}`).value = '';
});
}
function fillInitialConditionsForm(initialConditions) {
document.getElementById('satellite-pdcm-ic-time').value =
formatDateTimeInput(initialConditions?.ic?.orbPoint?.time) || currentDateTimeInputValue();
document.getElementById('satellite-pdcm-ic-revolution').value =
initialConditions?.ic?.orbPoint?.revolution ?? '';
document.getElementById('satellite-pdcm-ic-movement-model').value =
initialConditions?.movementModel || 'FOTO';
document.getElementById('satellite-pdcm-ic-s-ball').value =
initialConditions?.ic?.sBall ?? '0';
document.getElementById('satellite-pdcm-ic-f81').value =
initialConditions?.ic?.f81 ?? '147.8';
document.getElementById('satellite-pdcm-ic-x').value = initialConditions?.ic?.orbPoint?.x ?? '';
document.getElementById('satellite-pdcm-ic-y').value = initialConditions?.ic?.orbPoint?.y ?? '';
document.getElementById('satellite-pdcm-ic-z').value = initialConditions?.ic?.orbPoint?.z ?? '';
document.getElementById('satellite-pdcm-ic-vx').value = initialConditions?.ic?.orbPoint?.vx ?? '';
document.getElementById('satellite-pdcm-ic-vy').value = initialConditions?.ic?.orbPoint?.vy ?? '';
document.getElementById('satellite-pdcm-ic-vz').value = initialConditions?.ic?.orbPoint?.vz ?? '';
}
async function openSetInitialConditionsModal() {
if (!state.selectedSatelliteId) {
showAlert('Выберите спутник для задания начальных условий');
return;
}
const selectedSatellite = state.details?.satelliteId === state.selectedSatelliteId
? state.details
: state.satellites.find(satellite => satellite.satelliteId === state.selectedSatelliteId);
document.getElementById('satellite-pdcm-set-ic-target').textContent =
selectedSatellite
? `${selectedSatellite.name} · ID ${selectedSatellite.satelliteId}`
: `ID ${state.selectedSatelliteId}`;
resetInitialConditionsForm();
if (window.bootstrap) {
window.bootstrap.Modal.getOrCreateInstance(document.getElementById('satellite-pdcm-set-ic-modal')).show();
}
try {
const currentInitialConditions = await requestJson(`${apiBase}/${state.selectedSatelliteId}/initial-conditions`);
if (currentInitialConditions) {
fillInitialConditionsForm(currentInitialConditions);
}
} catch (error) {
showAlert(error.message || 'Не удалось загрузить актуальные начальные условия');
}
}
function buildInitialConditionsPayload() {
@@ -475,7 +528,7 @@
async function submitInitialConditions(event) {
event.preventDefault();
if (!state.selectedSatelliteId || !state.details) {
if (!state.selectedSatelliteId) {
showAlert('Выберите спутник для задания начальных условий');
return;
}
@@ -509,6 +562,7 @@
async function selectSatellite(satelliteId) {
state.selectedSatelliteId = satelliteId;
state.details = null;
renderList();
renderSendInitialConditionsState(null);
renderSetInitialConditionsState(null);
@@ -523,6 +577,7 @@
state.details = null;
renderSummary(null);
renderTable(null);
renderSetInitialConditionsState(null);
showAlert(error.message || 'Не удалось загрузить ПДЦМ спутника');
}
}
@@ -538,12 +593,14 @@
if (nextSatelliteId) {
await selectSatellite(nextSatelliteId);
} else {
state.selectedSatelliteId = null;
state.details = null;
renderSummary(null);
renderTable(null);
}
} catch (error) {
state.satellites = [];
state.selectedSatelliteId = null;
state.details = null;
renderList();
renderSummary(null);
@@ -389,6 +389,41 @@ class CatalogControllerTest {
assertEquals(42L, requestCaptor.value.ic.orbPoint.revolution)
}
@Test
fun `satellite pdcm current initial conditions endpoint proxies request to ballistics`() {
val response = SatelliteICDTO(
satelliteId = 501L,
movementModel = MovementModel.KONDOR,
ic = InitialConditionsDTO(
orbPoint = OrbPointDTO(
time = LocalDateTime.of(2026, 4, 24, 10, 15, 30, 123_000_000),
revolution = 42L,
vx = 1.0,
vy = 2.0,
vz = 3.0,
x = 4.0,
y = 5.0,
z = 6.0
),
sBall = 0.07,
f81 = 145.2
)
)
doReturn(response).`when`(ballisticsService).currentInitialConditions(501L)
val actual = WebClient.create("http://localhost:$port")
.get()
.uri("/api/catalog/satellites/pdcm/501/initial-conditions")
.retrieve()
.bodyToMono(SatelliteICDTO::class.java)
.block()!!
assertEquals(501L, actual.satelliteId)
assertEquals(MovementModel.KONDOR, actual.movementModel)
assertEquals(42L, actual.ic.orbPoint.revolution)
verify(ballisticsService).currentInitialConditions(501L)
}
@Test
fun `satellite create endpoint proxies request`() {
val request = SatelliteCreateDTO(
@@ -59,6 +59,7 @@ class BallisticsServiceTest {
)
)
)
val currentInitialConditions = service.currentInitialConditions(101L)
assertEquals(
listOf(
@@ -67,7 +68,8 @@ class BallisticsServiceTest {
"/api/satellites/101/orbit",
"/api/satellites/orbit/availability",
"/api/satellites/101/extract-time",
"/api/satellites/receive-rv"
"/api/satellites/receive-rv",
"/api/satellites/101/initial-conditions/current"
),
requests.map { it.path }
)
@@ -78,6 +80,7 @@ class BallisticsServiceTest {
"time_start=2026-04-21T10:00&time_stop=2026-04-21T12:00",
null,
null,
null,
null
),
requests.map { it.query }
@@ -87,6 +90,8 @@ class BallisticsServiceTest {
assertNotNull(exactPoint)
assertEquals(timeStart, exactPoint.time)
assertEquals(true, requestBodies.single().contains(""""movementModel":"KONDOR""""))
assertNotNull(currentInitialConditions)
assertEquals(101L, currentInitialConditions.satelliteId)
}
private fun createService(serverUrl: String): BallisticsService {
@@ -133,6 +138,32 @@ class BallisticsServiceTest {
requestBodies.add(exchange.requestBody.bufferedReader().use { it.readText() })
respond(exchange, "")
}
startedServer.createContext("/api/satellites/101/initial-conditions/current") { exchange ->
requests.add(exchange.requestURI)
respond(
exchange,
"""
{
"satelliteId": 101,
"movementModel": "KONDOR",
"ic": {
"orbPoint": {
"time": "2026-04-21T10:00:00",
"revolution": 7,
"vx": 1.0,
"vy": 2.0,
"vz": 3.0,
"x": 4.0,
"y": 5.0,
"z": 6.0
},
"sBall": 0.0,
"f81": 147.8
}
}
""".trimIndent()
)
}
startedServer.start()
server = startedServer
return "http://localhost:${startedServer.address.port}"