From d693bf799d96744f5c90069ace6457208179acef Mon Sep 17 00:00:00 2001 From: emelianov Date: Fri, 29 May 2026 10:52:36 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=9D=D0=A3=20=D0=B8=D0=B7=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/ballistics/MovementModel.kt | 10 ++ .../dto/ballistics/SatelliteICDTO.kt | 6 +- .../controller/SatelliteController.kt | 12 ++ .../message/SatelliteIcRvKafkaListener.kt | 7 + .../service/SatelliteService.kt | 15 +- .../message/SatelliteIcRvKafkaListenerTest.kt | 6 +- .../controller/CatalogRestController.kt | 20 ++- .../service/BallisticsService.kt | 11 ++ .../resources/static/css/satellite-pdcm.css | 6 + .../static/satellite_pdcm_scripts.js | 145 ++++++++++++++++++ .../resources/templates/satellites-pdcm.html | 79 ++++++++++ .../controller/CatalogControllerTest.kt | 48 ++++++ .../service/BallisticsServiceTest.kt | 36 ++++- 13 files changed, 393 insertions(+), 8 deletions(-) create mode 100644 libs/pcp-types-lib/src/main/kotlin/space/nstart/pcp/pcp_types_lib/dto/ballistics/MovementModel.kt diff --git a/libs/pcp-types-lib/src/main/kotlin/space/nstart/pcp/pcp_types_lib/dto/ballistics/MovementModel.kt b/libs/pcp-types-lib/src/main/kotlin/space/nstart/pcp/pcp_types_lib/dto/ballistics/MovementModel.kt new file mode 100644 index 0000000..4f42263 --- /dev/null +++ b/libs/pcp-types-lib/src/main/kotlin/space/nstart/pcp/pcp_types_lib/dto/ballistics/MovementModel.kt @@ -0,0 +1,10 @@ +package space.nstart.pcp.pcp_types_lib.dto.ballistics + +enum class MovementModel { + FOTO, + KONDOR, + METEORM1, + METEORM2, + BARS, + KONDOR_PROGNOZ +} diff --git a/libs/pcp-types-lib/src/main/kotlin/space/nstart/pcp/pcp_types_lib/dto/ballistics/SatelliteICDTO.kt b/libs/pcp-types-lib/src/main/kotlin/space/nstart/pcp/pcp_types_lib/dto/ballistics/SatelliteICDTO.kt index 8481b4c..2dbe138 100644 --- a/libs/pcp-types-lib/src/main/kotlin/space/nstart/pcp/pcp_types_lib/dto/ballistics/SatelliteICDTO.kt +++ b/libs/pcp-types-lib/src/main/kotlin/space/nstart/pcp/pcp_types_lib/dto/ballistics/SatelliteICDTO.kt @@ -2,6 +2,8 @@ package space.nstart.pcp.pcp_types_lib.dto.ballistics class SatelliteICDTO( val satelliteId: Long = -1, - val ic : InitialConditionsDTO = InitialConditionsDTO() + val ic : InitialConditionsDTO = InitialConditionsDTO(), + movementModel: MovementModel? = MovementModel.FOTO ) { -} \ No newline at end of file + val movementModel: MovementModel = movementModel ?: MovementModel.FOTO +} diff --git a/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/controller/SatelliteController.kt b/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/controller/SatelliteController.kt index 925231e..e1c10bc 100644 --- a/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/controller/SatelliteController.kt +++ b/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/controller/SatelliteController.kt @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter import jakarta.validation.Valid import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -11,9 +12,11 @@ import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +import space.nstart.pcp.pcp_request_service.service.SatelliteIcEventService import space.nstart.pcp.pcp_request_service.service.SatelliteService import space.nstart.pcp.pcp_types_lib.configuration.CustomValidationException import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO +import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO import java.time.LocalDateTime @@ -23,6 +26,9 @@ class SatelliteController { @Autowired private lateinit var satelliteService: SatelliteService + @Autowired + private lateinit var satelliteIcEventService: SatelliteIcEventService + @PostMapping("/{satellite_id}/extract-time") fun extractTime( @PathVariable("satellite_id") satelliteId : Long, @@ -30,6 +36,12 @@ class SatelliteController { ) = satelliteService.exactTime(satelliteId, body) + @PostMapping("/receive-rv") + fun receiveRv(@RequestBody body: SatelliteICDTO): ResponseEntity { + satelliteIcEventService.handlePlacedIcRv(body) + return ResponseEntity.accepted().build() + } + @Operation( diff --git a/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/message/SatelliteIcRvKafkaListener.kt b/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/message/SatelliteIcRvKafkaListener.kt index f56e06e..22e6590 100644 --- a/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/message/SatelliteIcRvKafkaListener.kt +++ b/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/message/SatelliteIcRvKafkaListener.kt @@ -6,6 +6,7 @@ import org.springframework.kafka.annotation.KafkaListener import org.springframework.stereotype.Component import space.nstart.pcp.pcp_request_service.service.SatelliteIcEventService import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO +import space.nstart.pcp.pcp_types_lib.dto.ballistics.MovementModel import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO import tools.jackson.databind.ObjectMapper import tools.jackson.databind.node.ObjectNode @@ -52,6 +53,11 @@ class SatelliteIcRvKafkaListener( */ private fun normalizePayload(payloadNode: tools.jackson.databind.JsonNode): tools.jackson.databind.JsonNode { val normalizedPayload = payloadNode.deepCopy() + if (normalizedPayload is ObjectNode && + (normalizedPayload["movementModel"] == null || normalizedPayload["movementModel"].isNull) + ) { + normalizedPayload.put("movementModel", DEFAULT_MOVEMENT_MODEL.name) + } val initialConditionsNode = normalizedPayload["ic"] if (initialConditionsNode is ObjectNode) { if (initialConditionsNode["sBall"] == null || initialConditionsNode["sBall"].isNull) { @@ -66,5 +72,6 @@ class SatelliteIcRvKafkaListener( companion object { private val DEFAULT_INITIAL_CONDITIONS = InitialConditionsDTO() + private val DEFAULT_MOVEMENT_MODEL = MovementModel.FOTO } } diff --git a/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/service/SatelliteService.kt b/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/service/SatelliteService.kt index c270b3b..7a17ad1 100644 --- a/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/service/SatelliteService.kt +++ b/services/pcp-ballistics-service/src/main/kotlin/space/nstart/pcp/pcp_request_service/service/SatelliteService.kt @@ -7,6 +7,7 @@ import ballistics.types.EarthType import ballistics.types.FleghtLineSector import ballistics.types.InitialConditions import ballistics.types.KeplerParams +import ballistics.types.ModDVType import ballistics.types.OPKatObj import ballistics.types.OrbitalPoint import ballistics.types.PPI @@ -46,6 +47,7 @@ import space.nstart.pcp.pcp_request_service.utils.ContourClipService import space.nstart.pcp.pcp_types_lib.dto.ballistics.AscNodeDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO +import space.nstart.pcp.pcp_types_lib.dto.ballistics.MovementModel import space.nstart.pcp.pcp_types_lib.dto.ballistics.ObjDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.PointViewParamDTO @@ -184,8 +186,9 @@ class SatelliteService { suspend fun resieveRV(rv: SatelliteICDTO) { val bal = Ballistics() + bal.modDVType = rv.movementModel.toModDVType() - logger.info("Начало расчета баллистики по RV id = ${rv.satelliteId}") + logger.info("Начало расчета баллистики по RV id = ${rv.satelliteId}, model = ${rv.movementModel}") bal.rollMax = (45.0 * PI / 180.0) bal.rollMin = (20.0 * PI / 180.0) @@ -231,6 +234,16 @@ class SatelliteService { logger.info("Расчет для КА ${rv.satelliteId} завершился успешно") } + private fun MovementModel.toModDVType(): ModDVType = + when (this) { + MovementModel.FOTO -> ModDVType.FOTO + MovementModel.KONDOR -> ModDVType.KONDOR + MovementModel.METEORM1 -> ModDVType.METEORM1 + MovementModel.METEORM2 -> ModDVType.METEORM2 + MovementModel.BARS -> ModDVType.BARS + MovementModel.KONDOR_PROGNOZ -> ModDVType.KONDOR_PROGNOZ + } + fun mergeSectors(sectors: List): List { if (sectors.isEmpty()) return emptyList() diff --git a/services/pcp-ballistics-service/src/test/kotlin/space/nstart/pcp/pcp_request_service/message/SatelliteIcRvKafkaListenerTest.kt b/services/pcp-ballistics-service/src/test/kotlin/space/nstart/pcp/pcp_request_service/message/SatelliteIcRvKafkaListenerTest.kt index 0d712a7..be3e009 100644 --- a/services/pcp-ballistics-service/src/test/kotlin/space/nstart/pcp/pcp_request_service/message/SatelliteIcRvKafkaListenerTest.kt +++ b/services/pcp-ballistics-service/src/test/kotlin/space/nstart/pcp/pcp_request_service/message/SatelliteIcRvKafkaListenerTest.kt @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.springframework.beans.factory.ObjectProvider import space.nstart.pcp.pcp_request_service.service.SatelliteIcEventService +import space.nstart.pcp.pcp_types_lib.dto.ballistics.MovementModel import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO import tools.jackson.databind.ObjectMapper @@ -46,6 +47,7 @@ class SatelliteIcRvKafkaListenerTest { assertEquals(56756L, satelliteIcEventService.lastMessage?.satelliteId) assertEquals(42L, satelliteIcEventService.lastMessage?.ic?.orbPoint?.revolution) assertEquals(140.0, satelliteIcEventService.lastMessage?.ic?.f81) + assertEquals(MovementModel.FOTO, satelliteIcEventService.lastMessage?.movementModel) } @Test @@ -54,8 +56,9 @@ class SatelliteIcRvKafkaListenerTest { """ { "type": "ICRVPlacedEvent", - "data": { + "data": { "satelliteId": 56756, + "movementModel": null, "ic": { "orbPoint": { "time": "2026-04-16T12:00:00", @@ -77,6 +80,7 @@ class SatelliteIcRvKafkaListenerTest { assertEquals(0.0, satelliteIcEventService.lastMessage?.ic?.sBall) assertEquals(147.8, satelliteIcEventService.lastMessage?.ic?.f81) + assertEquals(MovementModel.FOTO, satelliteIcEventService.lastMessage?.movementModel) } private class RecordingSatelliteIcEventService : SatelliteIcEventService() { diff --git a/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/controller/CatalogRestController.kt b/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/controller/CatalogRestController.kt index 5600791..643b469 100644 --- a/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/controller/CatalogRestController.kt +++ b/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/controller/CatalogRestController.kt @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import org.springframework.web.server.ResponseStatusException import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO +import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO import space.nstart.pcp.pcp_types_lib.dto.requests.slots.SlotCalculationSummaryDTO import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteCreateDTO import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteGroupCreateDTO @@ -22,6 +23,7 @@ import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteObservationProfileD import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteSlotProfileDTO import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteUpdateDTO import space.nstart.pcp.slots_service.service.GroupStateService +import space.nstart.pcp.slots_service.service.BallisticsService import space.nstart.pcp.slots_service.service.SatelliteCatalogService import space.nstart.pcp.slots_service.service.SatellitePdcmService import space.nstart.pcp.slots_service.service.SlotService @@ -36,7 +38,8 @@ class CatalogRestController( private val satelliteCatalogService: SatelliteCatalogService, private val slotService: SlotService, private val groupStateService: GroupStateService, - private val satellitePdcmService: SatellitePdcmService + private val satellitePdcmService: SatellitePdcmService, + private val ballisticsService: BallisticsService ) { private val sendInitialConditionsTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.uuuu HH:mm:ss.SSS").withResolverStyle(ResolverStyle.STRICT) @@ -73,6 +76,21 @@ class CatalogRestController( return ResponseEntity.accepted().build() } + @PostMapping("/satellites/pdcm/{satelliteId}/initial-conditions") + fun setSatelliteInitialConditions( + @PathVariable satelliteId: Long, + @RequestBody request: SatelliteICDTO + ): ResponseEntity { + ballisticsService.receiveRv( + SatelliteICDTO( + satelliteId = satelliteId, + ic = request.ic, + movementModel = request.movementModel + ) + ) + return ResponseEntity.accepted().build() + } + private fun parseSendInitialConditionsTime(time: String?): LocalDateTime { val normalizedTime = time?.trim()?.takeIf { it.isNotEmpty() } ?: return LocalDateTime.now() diff --git a/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/service/BallisticsService.kt b/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/service/BallisticsService.kt index 1b4865e..77e6ede 100644 --- a/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/service/BallisticsService.kt +++ b/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/service/BallisticsService.kt @@ -9,6 +9,7 @@ import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.AscNodeDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.FlightLineDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO +import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteOrbitAvailabilityDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.TLEDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.TlePointRequestDTO @@ -71,6 +72,16 @@ class BallisticsService (webClientBuilderProvider: ObjectProvider { + document.getElementById(`satellite-pdcm-ic-${field}`).value = ''; + }); + + if (window.bootstrap) { + window.bootstrap.Modal.getOrCreateInstance(document.getElementById('satellite-pdcm-set-ic-modal')).show(); + } + } + + function buildInitialConditionsPayload() { + const movementModel = document.getElementById('satellite-pdcm-ic-movement-model').value || 'FOTO'; + return { + satelliteId: state.selectedSatelliteId, + movementModel, + ic: { + orbPoint: { + time: parseDateTimeForPayload(document.getElementById('satellite-pdcm-ic-time').value, 'Time'), + revolution: parseRequiredInteger('satellite-pdcm-ic-revolution', 'Revolution'), + vx: parseRequiredNumber('satellite-pdcm-ic-vx', 'Vx'), + vy: parseRequiredNumber('satellite-pdcm-ic-vy', 'Vy'), + vz: parseRequiredNumber('satellite-pdcm-ic-vz', 'Vz'), + x: parseRequiredNumber('satellite-pdcm-ic-x', 'X'), + y: parseRequiredNumber('satellite-pdcm-ic-y', 'Y'), + z: parseRequiredNumber('satellite-pdcm-ic-z', 'Z') + }, + sBall: parseRequiredNumber('satellite-pdcm-ic-s-ball', 'S-ball'), + f81: parseRequiredNumber('satellite-pdcm-ic-f81', 'F81') + } + }; + } + + async function submitInitialConditions(event) { + event.preventDefault(); + if (!state.selectedSatelliteId || !state.details) { + showAlert('Выберите спутник для задания начальных условий'); + return; + } + + let payload; + try { + payload = buildInitialConditionsPayload(); + } catch (error) { + showAlert(error.message || 'Проверьте параметры начальных условий'); + return; + } + + renderSetInitialConditionsState(state.details, true); + showAlert(''); + try { + await requestJson(`${apiBase}/${state.selectedSatelliteId}/initial-conditions`, { + method: 'POST', + body: JSON.stringify(payload) + }); + const modalElement = document.getElementById('satellite-pdcm-set-ic-modal'); + if (window.bootstrap && modalElement) { + window.bootstrap.Modal.getOrCreateInstance(modalElement).hide(); + } + showAlert('Расчет ПДЦМ по начальным условиям запущен', 'success'); + } catch (error) { + showAlert(error.message || 'Не удалось запустить расчет ПДЦМ по начальным условиям'); + } finally { + renderSetInitialConditionsState(state.details); + } + } + async function selectSatellite(satelliteId) { state.selectedSatelliteId = satelliteId; renderList(); renderSendInitialConditionsState(null); + renderSetInitialConditionsState(null); showAlert(''); try { @@ -414,6 +557,8 @@ }); document.getElementById('satellite-pdcm-export').addEventListener('click', exportCurrentCsv); document.getElementById('satellite-pdcm-send-ic').addEventListener('click', sendInitialConditions); + document.getElementById('satellite-pdcm-open-set-ic').addEventListener('click', openSetInitialConditionsModal); + document.getElementById('satellite-pdcm-set-ic-form').addEventListener('submit', submitInitialConditions); const sendInitialConditionsTimeInput = document.getElementById('satellite-pdcm-send-ic-time'); sendInitialConditionsTimeInput.value = currentDateTimeInputValue(); sendInitialConditionsTimeInput.addEventListener('input', () => renderSendInitialConditionsState(state.details)); diff --git a/services/pcp-ui-service/src/main/resources/templates/satellites-pdcm.html b/services/pcp-ui-service/src/main/resources/templates/satellites-pdcm.html index b33487b..0e2c455 100644 --- a/services/pcp-ui-service/src/main/resources/templates/satellites-pdcm.html +++ b/services/pcp-ui-service/src/main/resources/templates/satellites-pdcm.html @@ -28,6 +28,7 @@ /> + @@ -80,6 +81,84 @@ + + diff --git a/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/controller/CatalogControllerTest.kt b/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/controller/CatalogControllerTest.kt index 50c0c28..c303bfb 100644 --- a/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/controller/CatalogControllerTest.kt +++ b/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/controller/CatalogControllerTest.kt @@ -1,6 +1,7 @@ package space.nstart.pcp.slots_service.controller import org.junit.jupiter.api.Test +import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito.doReturn @@ -15,6 +16,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean import org.springframework.web.reactive.function.client.WebClient import reactor.core.publisher.Mono import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO +import space.nstart.pcp.pcp_types_lib.dto.ballistics.MovementModel import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO import space.nstart.pcp.pcp_types_lib.dto.ballistics.PositionDTO @@ -34,6 +36,7 @@ import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteGroupCreateDTO import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteGroupDTO import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteSlotProfileDTO import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteVisualizationDTO +import space.nstart.pcp.slots_service.service.BallisticsService import space.nstart.pcp.slots_service.service.GroupStateService import space.nstart.pcp.slots_service.service.SatelliteCatalogService import space.nstart.pcp.slots_service.service.SatellitePdcmService @@ -73,6 +76,9 @@ class CatalogControllerTest { @MockitoBean private lateinit var satellitePdcmService: SatellitePdcmService + @MockitoBean + private lateinit var ballisticsService: BallisticsService + @Test fun `groups page is rendered`() { val body = WebClient.create("http://localhost:$port") @@ -136,6 +142,9 @@ class CatalogControllerTest { assertTrue(body.contains("Спутники")) assertTrue(body.contains("Скачать CSV")) assertTrue(body.contains("satellite-pdcm-send-ic-time")) + assertTrue(body.contains("Задать НУ")) + assertTrue(body.contains("satellite-pdcm-set-ic-modal")) + assertTrue(body.contains("value=\"FOTO\" selected")) assertTrue(body.contains("dd.mm.yyyy hh:MM:ss.zzz")) assertTrue(body.contains("/webjars/bootstrap/5.3.0/css/bootstrap.min.css")) } @@ -343,6 +352,43 @@ class CatalogControllerTest { verify(slotService, never()).sendSatelliteInitialConditions(eq(501L), anyLocalDateTime()) } + @Test + fun `satellite pdcm set initial conditions endpoint proxies request to ballistics`() { + val request = SatelliteICDTO( + satelliteId = 999L, + 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 + ) + ) + + val status = WebClient.create("http://localhost:$port") + .post() + .uri("/api/catalog/satellites/pdcm/501/initial-conditions") + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(request) + .exchangeToMono { response -> Mono.just(response.statusCode()) } + .block()!! + + val requestCaptor = ArgumentCaptor.forClass(SatelliteICDTO::class.java) + assertEquals(HttpStatus.ACCEPTED, status) + verify(ballisticsService).receiveRv(captureSatelliteIc(requestCaptor)) + assertEquals(501L, requestCaptor.value.satelliteId) + assertEquals(MovementModel.KONDOR, requestCaptor.value.movementModel) + assertEquals(42L, requestCaptor.value.ic.orbPoint.revolution) + } + @Test fun `satellite create endpoint proxies request`() { val request = SatelliteCreateDTO( @@ -578,4 +624,6 @@ class CatalogControllerTest { private fun anyInitialConditions(): InitialConditionsDTO = any(InitialConditionsDTO::class.java) ?: InitialConditionsDTO() private fun anySlotProfile(): SatelliteSlotProfileDTO = any(SatelliteSlotProfileDTO::class.java) ?: SatelliteSlotProfileDTO() private fun anyLocalDateTime(): LocalDateTime = any(LocalDateTime::class.java) ?: LocalDateTime.MIN + private fun captureSatelliteIc(captor: ArgumentCaptor): SatelliteICDTO = + captor.capture() ?: SatelliteICDTO() } diff --git a/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/BallisticsServiceTest.kt b/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/BallisticsServiceTest.kt index 17b191e..6515bcc 100644 --- a/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/BallisticsServiceTest.kt +++ b/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/BallisticsServiceTest.kt @@ -7,7 +7,10 @@ import org.junit.jupiter.api.Test import org.springframework.beans.factory.ObjectProvider import org.springframework.test.util.ReflectionTestUtils import org.springframework.web.reactive.function.client.WebClient +import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO +import space.nstart.pcp.pcp_types_lib.dto.ballistics.MovementModel import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO +import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO import java.net.InetSocketAddress import java.net.URI import java.time.LocalDateTime @@ -27,7 +30,8 @@ class BallisticsServiceTest { @Test fun `sat data methods forward time interval to ballistics service`() { val requests = CopyOnWriteArrayList() - val serverUrl = startServer(requests) + val requestBodies = CopyOnWriteArrayList() + val serverUrl = startServer(requests, requestBodies) val service = createService(serverUrl) val timeStart = LocalDateTime.of(2026, 4, 21, 10, 0) val timeStop = LocalDateTime.of(2026, 4, 21, 12, 0) @@ -37,6 +41,24 @@ class BallisticsServiceTest { service.points(101L, timeStart, timeStop).collectList().block() val availability = service.orbitAvailability() val exactPoint = service.exactTimePoint(101L, timeStart) + service.receiveRv( + SatelliteICDTO( + satelliteId = 101L, + movementModel = MovementModel.KONDOR, + ic = InitialConditionsDTO( + orbPoint = OrbPointDTO( + time = timeStart, + revolution = 7L, + vx = 1.0, + vy = 2.0, + vz = 3.0, + x = 4.0, + y = 5.0, + z = 6.0 + ) + ) + ) + ) assertEquals( listOf( @@ -44,7 +66,8 @@ class BallisticsServiceTest { "/api/satellites/101/flight-line", "/api/satellites/101/orbit", "/api/satellites/orbit/availability", - "/api/satellites/101/extract-time" + "/api/satellites/101/extract-time", + "/api/satellites/receive-rv" ), requests.map { it.path } ) @@ -54,6 +77,7 @@ class BallisticsServiceTest { "time_start=2026-04-21T10:00&time_stop=2026-04-21T12:00", "time_start=2026-04-21T10:00&time_stop=2026-04-21T12:00", null, + null, null ), requests.map { it.query } @@ -62,6 +86,7 @@ class BallisticsServiceTest { assertEquals(101L, availability.single().satelliteId) assertNotNull(exactPoint) assertEquals(timeStart, exactPoint.time) + assertEquals(true, requestBodies.single().contains(""""movementModel":"KONDOR"""")) } private fun createService(serverUrl: String): BallisticsService { @@ -77,7 +102,7 @@ class BallisticsServiceTest { return service } - private fun startServer(requests: MutableList): String { + private fun startServer(requests: MutableList, requestBodies: MutableList): String { val startedServer = HttpServer.create(InetSocketAddress(0), 0) listOf( "/api/satellites/101/asc-node", @@ -103,6 +128,11 @@ class BallisticsServiceTest { """[{"time":"2026-04-21T10:00:00","revolution":1,"vx":1.0,"vy":2.0,"vz":3.0,"x":4.0,"y":5.0,"z":6.0}]""" ) } + startedServer.createContext("/api/satellites/receive-rv") { exchange -> + requests.add(exchange.requestURI) + requestBodies.add(exchange.requestBody.bufferedReader().use { it.readText() }) + respond(exchange, "") + } startedServer.start() server = startedServer return "http://localhost:${startedServer.address.port}"