Задание НУ из 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
@@ -42,6 +42,14 @@ class SatelliteController {
return ResponseEntity.accepted().build() return ResponseEntity.accepted().build()
} }
@GetMapping("/{satellite_id}/initial-conditions/current")
fun currentInitialConditions(
@PathVariable("satellite_id") satelliteId: Long
): ResponseEntity<SatelliteICDTO> =
satelliteService.currentInitialConditions(satelliteId)
?.let { ResponseEntity.ok(it) }
?: ResponseEntity.noContent().build()
@Operation( @Operation(
@@ -0,0 +1,94 @@
package space.nstart.pcp.pcp_request_service.entity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
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.time.LocalDateTime
@Entity
@Table(name = "satellite_initial_conditions")
class SatelliteInitialConditionEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "satellite_initial_condition_id")
val id: Long? = null,
@Column(name = "satellite_id", nullable = false)
val satelliteId: Long = 0,
@Enumerated(EnumType.STRING)
@Column(name = "movement_model", nullable = false)
val movementModel: MovementModel = MovementModel.FOTO,
@Column(name = "orb_time", nullable = false)
val orbTime: LocalDateTime = LocalDateTime.now(),
@Column(name = "orb_revolution", nullable = false)
val orbRevolution: Long = 0,
@Column(nullable = false)
val x: Double = 0.0,
@Column(nullable = false)
val y: Double = 0.0,
@Column(nullable = false)
val z: Double = 0.0,
@Column(nullable = false)
val vx: Double = 0.0,
@Column(nullable = false)
val vy: Double = 0.0,
@Column(nullable = false)
val vz: Double = 0.0,
@Column(name = "s_ball", nullable = false)
val sBall: Double = 0.0,
@Column(nullable = false)
val f81: Double = 147.8,
@Column(name = "received_at", nullable = false)
val receivedAt: LocalDateTime = LocalDateTime.now(),
@Column(name = "calculated_at")
var calculatedAt: LocalDateTime? = null,
@Column(name = "last_calculated", nullable = false)
var lastCalculated: Boolean = false
) {
constructor(request: SatelliteICDTO) : this(
satelliteId = request.satelliteId,
movementModel = request.movementModel,
orbTime = request.ic.orbPoint.time,
orbRevolution = request.ic.orbPoint.revolution,
x = request.ic.orbPoint.x,
y = request.ic.orbPoint.y,
z = request.ic.orbPoint.z,
vx = request.ic.orbPoint.vx,
vy = request.ic.orbPoint.vy,
vz = request.ic.orbPoint.vz,
sBall = request.ic.sBall,
f81 = request.ic.f81
)
fun markLastCalculated(calculatedAt: LocalDateTime = LocalDateTime.now()) {
this.calculatedAt = calculatedAt
this.lastCalculated = true
}
fun toDto() = SatelliteICDTO(
satelliteId = satelliteId,
movementModel = movementModel,
ic = InitialConditionsDTO(
orbPoint = OrbPointDTO(
time = orbTime,
revolution = orbRevolution,
vx = vx,
vy = vy,
vz = vz,
x = x,
y = y,
z = z
),
sBall = sBall,
f81 = f81
)
)
}
@@ -0,0 +1,26 @@
package space.nstart.pcp.pcp_request_service.repository
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Modifying
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp.pcp_request_service.entity.SatelliteInitialConditionEntity
interface SatelliteInitialConditionRepository : JpaRepository<SatelliteInitialConditionEntity, Long> {
fun findFirstBySatelliteIdAndLastCalculatedTrueOrderByCalculatedAtDescIdDesc(
satelliteId: Long
): SatelliteInitialConditionEntity?
@Modifying
@Transactional
@Query(
"""
update SatelliteInitialConditionEntity entity
set entity.lastCalculated = false
where entity.satelliteId = :satelliteId
and entity.lastCalculated = true
"""
)
fun clearLastCalculated(@Param("satelliteId") satelliteId: Long): Int
}
@@ -0,0 +1,30 @@
package space.nstart.pcp.pcp_request_service.service
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp.pcp_request_service.entity.SatelliteInitialConditionEntity
import space.nstart.pcp.pcp_request_service.repository.SatelliteInitialConditionRepository
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO
@Service
class SatelliteInitialConditionService(
private val satelliteInitialConditionRepository: SatelliteInitialConditionRepository
) {
@Transactional
fun saveReceived(request: SatelliteICDTO): SatelliteInitialConditionEntity =
satelliteInitialConditionRepository.save(SatelliteInitialConditionEntity(request))
@Transactional
fun markLastCalculated(initialCondition: SatelliteInitialConditionEntity) {
satelliteInitialConditionRepository.clearLastCalculated(initialCondition.satelliteId)
initialCondition.markLastCalculated()
satelliteInitialConditionRepository.save(initialCondition)
}
@Transactional(readOnly = true)
fun current(satelliteId: Long): SatelliteICDTO? =
satelliteInitialConditionRepository
.findFirstBySatelliteIdAndLastCalculatedTrueOrderByCalculatedAtDescIdDesc(satelliteId)
?.toDto()
}
@@ -89,6 +89,10 @@ class SatelliteService {
@Autowired @Autowired
private lateinit var pdcmRepository: PDCMRepository private lateinit var pdcmRepository: PDCMRepository
@Autowired
private lateinit var satelliteInitialConditionService: SatelliteInitialConditionService
@Autowired @Autowired
private lateinit var ascNodeRepository: AscNodeRepository private lateinit var ascNodeRepository: AscNodeRepository
@Autowired @Autowired
@@ -185,6 +189,7 @@ class SatelliteService {
) )
suspend fun resieveRV(rv: SatelliteICDTO) { suspend fun resieveRV(rv: SatelliteICDTO) {
val savedInitialCondition = satelliteInitialConditionService.saveReceived(rv)
val bal = Ballistics() val bal = Ballistics()
bal.modDVType = rv.movementModel.toModDVType() bal.modDVType = rv.movementModel.toModDVType()
@@ -230,10 +235,15 @@ class SatelliteService {
prepareEarthCovering(bal, rv.satelliteId) prepareEarthCovering(bal, rv.satelliteId)
satelliteInitialConditionService.markLastCalculated(savedInitialCondition)
bal.clear() bal.clear()
logger.info("Расчет для КА ${rv.satelliteId} завершился успешно") logger.info("Расчет для КА ${rv.satelliteId} завершился успешно")
} }
fun currentInitialConditions(satelliteId: Long): SatelliteICDTO? =
satelliteInitialConditionService.current(satelliteId)
private fun MovementModel.toModDVType(): ModDVType = private fun MovementModel.toModDVType(): ModDVType =
when (this) { when (this) {
MovementModel.FOTO -> ModDVType.FOTO MovementModel.FOTO -> ModDVType.FOTO
@@ -893,3 +903,16 @@ data class SatelliteBallisticsDeletionSummary(
val earthCoverageDeleted: Int, val earthCoverageDeleted: Int,
val earthCellViewsDeleted: Int val earthCellViewsDeleted: Int
) )
//DT: 11.12.2028 01:24:24.257000000 MDT
//x= 2610612.004898679
//y= -3507514.947081291
//z= -5291534.374006776
//Vx= -4935.515395115775
//Vy= 3497.047795669036
//Vz= -4742.501356855924
//CS: GSK
//Sb= 0.06
//kappa= 0
@@ -0,0 +1,21 @@
CREATE TABLE IF NOT EXISTS satellite_initial_conditions (
satellite_initial_condition_id BIGSERIAL PRIMARY KEY,
satellite_id BIGINT NOT NULL,
movement_model VARCHAR(32) NOT NULL DEFAULT 'FOTO',
orb_time TIMESTAMP NOT NULL,
orb_revolution BIGINT NOT NULL,
x DOUBLE PRECISION NOT NULL,
y DOUBLE PRECISION NOT NULL,
z DOUBLE PRECISION NOT NULL,
vx DOUBLE PRECISION NOT NULL,
vy DOUBLE PRECISION NOT NULL,
vz DOUBLE PRECISION NOT NULL,
s_ball DOUBLE PRECISION NOT NULL,
f81 DOUBLE PRECISION NOT NULL,
received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
calculated_at TIMESTAMP,
last_calculated BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX idx_satellite_initial_conditions_satellite_id ON satellite_initial_conditions(satellite_id);
CREATE INDEX idx_satellite_initial_conditions_last_calculated ON satellite_initial_conditions(satellite_id, last_calculated);
@@ -14,6 +14,11 @@ import org.springframework.kafka.core.DefaultKafkaConsumerFactory
import org.springframework.kafka.listener.adapter.RecordFilterStrategy import org.springframework.kafka.listener.adapter.RecordFilterStrategy
import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp.pcp_request_service.entity.PDCMEntity import space.nstart.pcp.pcp_request_service.entity.PDCMEntity
import space.nstart.pcp.pcp_request_service.entity.SatelliteInitialConditionEntity
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.time.LocalDateTime import java.time.LocalDateTime
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -41,6 +46,9 @@ class PDCMRepositoryTest {
@Autowired @Autowired
private lateinit var repository: PDCMRepository private lateinit var repository: PDCMRepository
@Autowired
private lateinit var satelliteInitialConditionRepository: SatelliteInitialConditionRepository
@Test @Test
fun `find satellite orbit availability returns all satellites with points when time is null`() { fun `find satellite orbit availability returns all satellites with points when time is null`() {
persistPoint(11L, LocalDateTime.of(2026, 4, 14, 10, 0)) persistPoint(11L, LocalDateTime.of(2026, 4, 14, 10, 0))
@@ -74,6 +82,34 @@ class PDCMRepositoryTest {
assertEquals(LocalDateTime.of(2026, 4, 14, 12, 0), actual.single().timeStop) assertEquals(LocalDateTime.of(2026, 4, 14, 12, 0), actual.single().timeStop)
} }
@Test
fun `satellite initial conditions repository returns last calculated condition`() {
val oldCondition = satelliteInitialConditionRepository.save(sampleInitialCondition(11L, 1L))
oldCondition.markLastCalculated(LocalDateTime.of(2026, 4, 14, 10, 0))
satelliteInitialConditionRepository.save(oldCondition)
val newCondition = satelliteInitialConditionRepository.save(sampleInitialCondition(11L, 2L))
satelliteInitialConditionRepository.clearLastCalculated(11L)
newCondition.markLastCalculated(LocalDateTime.of(2026, 4, 14, 12, 0))
satelliteInitialConditionRepository.save(newCondition)
satelliteInitialConditionRepository.save(sampleInitialCondition(11L, 3L))
entityManager.flush()
entityManager.clear()
val actual = satelliteInitialConditionRepository
.findFirstBySatelliteIdAndLastCalculatedTrueOrderByCalculatedAtDescIdDesc(11L)
assertEquals(2L, actual?.toDto()?.ic?.orbPoint?.revolution)
assertEquals(MovementModel.KONDOR, actual?.movementModel)
assertEquals(
1,
satelliteInitialConditionRepository.findAll().count { entity ->
entity.satelliteId == 11L && entity.lastCalculated
}
)
}
private fun persistPoint(satelliteId: Long, time: LocalDateTime) { private fun persistPoint(satelliteId: Long, time: LocalDateTime) {
entityManager.persist( entityManager.persist(
PDCMEntity( PDCMEntity(
@@ -91,6 +127,28 @@ class PDCMRepositoryTest {
entityManager.flush() entityManager.flush()
} }
private fun sampleInitialCondition(satelliteId: Long, revolution: Long) =
SatelliteInitialConditionEntity(
SatelliteICDTO(
satelliteId = satelliteId,
movementModel = MovementModel.KONDOR,
ic = InitialConditionsDTO(
orbPoint = OrbPointDTO(
time = LocalDateTime.of(2026, 4, 14, 10, 0).plusHours(revolution),
revolution = revolution,
vx = 1.0,
vy = 2.0,
vz = 3.0,
x = 4.0,
y = 5.0,
z = 6.0
),
sBall = 0.2,
f81 = 141.0
)
)
)
@TestConfiguration @TestConfiguration
class KafkaFilterStubConfiguration { class KafkaFilterStubConfiguration {
@Bean("kafkaListenerContainerFactory") @Bean("kafkaListenerContainerFactory")
@@ -91,6 +91,16 @@ class CatalogRestController(
return ResponseEntity.accepted().build() 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 { private fun parseSendInitialConditionsTime(time: String?): LocalDateTime {
val normalizedTime = time?.trim()?.takeIf { it.isNotEmpty() } val normalizedTime = time?.trim()?.takeIf { it.isNotEmpty() }
?: return LocalDateTime.now() ?: return LocalDateTime.now()
@@ -82,6 +82,19 @@ class BallisticsService (webClientBuilderProvider: ObjectProvider<WebClient.Buil
.block() .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 = fun parseTle(tle: TLEDTO): TleParsedDTO =
webClientBuilder.build() webClientBuilder.build()
.post() .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)}`; 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() { function parseSendInitialConditionsTime() {
const input = document.getElementById('satellite-pdcm-send-ic-time'); const input = document.getElementById('satellite-pdcm-send-ic-time');
const value = input?.value.trim() || ''; const value = input?.value.trim() || '';
@@ -194,9 +209,10 @@
function renderSetInitialConditionsState(details, loading = false) { function renderSetInitialConditionsState(details, loading = false) {
const openButton = document.getElementById('satellite-pdcm-open-set-ic'); const openButton = document.getElementById('satellite-pdcm-open-set-ic');
const submitButton = document.getElementById('satellite-pdcm-set-ic-submit'); const submitButton = document.getElementById('satellite-pdcm-set-ic-submit');
const satelliteSelected = !!state.selectedSatelliteId;
if (openButton) { if (openButton) {
openButton.disabled = !details || loading; openButton.disabled = !satelliteSelected || loading;
openButton.title = details ? '' : 'Выберите спутник'; openButton.title = satelliteSelected ? '' : 'Выберите спутник';
} }
if (submitButton) { if (submitButton) {
submitButton.disabled = loading; submitButton.disabled = loading;
@@ -429,14 +445,7 @@
} }
} }
function openSetInitialConditionsModal() { function resetInitialConditionsForm() {
if (!state.selectedSatelliteId || !state.details) {
showAlert('Выберите спутник для задания начальных условий');
return;
}
document.getElementById('satellite-pdcm-set-ic-target').textContent =
`${state.details.name} · ID ${state.details.satelliteId}`;
document.getElementById('satellite-pdcm-ic-time').value = currentDateTimeInputValue(); document.getElementById('satellite-pdcm-ic-time').value = currentDateTimeInputValue();
document.getElementById('satellite-pdcm-ic-revolution').value = ''; document.getElementById('satellite-pdcm-ic-revolution').value = '';
document.getElementById('satellite-pdcm-ic-movement-model').value = 'FOTO'; document.getElementById('satellite-pdcm-ic-movement-model').value = 'FOTO';
@@ -445,10 +454,54 @@
['x', 'y', 'z', 'vx', 'vy', 'vz'].forEach(field => { ['x', 'y', 'z', 'vx', 'vy', 'vz'].forEach(field => {
document.getElementById(`satellite-pdcm-ic-${field}`).value = ''; 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) { if (window.bootstrap) {
window.bootstrap.Modal.getOrCreateInstance(document.getElementById('satellite-pdcm-set-ic-modal')).show(); 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() { function buildInitialConditionsPayload() {
@@ -475,7 +528,7 @@
async function submitInitialConditions(event) { async function submitInitialConditions(event) {
event.preventDefault(); event.preventDefault();
if (!state.selectedSatelliteId || !state.details) { if (!state.selectedSatelliteId) {
showAlert('Выберите спутник для задания начальных условий'); showAlert('Выберите спутник для задания начальных условий');
return; return;
} }
@@ -509,6 +562,7 @@
async function selectSatellite(satelliteId) { async function selectSatellite(satelliteId) {
state.selectedSatelliteId = satelliteId; state.selectedSatelliteId = satelliteId;
state.details = null;
renderList(); renderList();
renderSendInitialConditionsState(null); renderSendInitialConditionsState(null);
renderSetInitialConditionsState(null); renderSetInitialConditionsState(null);
@@ -523,6 +577,7 @@
state.details = null; state.details = null;
renderSummary(null); renderSummary(null);
renderTable(null); renderTable(null);
renderSetInitialConditionsState(null);
showAlert(error.message || 'Не удалось загрузить ПДЦМ спутника'); showAlert(error.message || 'Не удалось загрузить ПДЦМ спутника');
} }
} }
@@ -538,12 +593,14 @@
if (nextSatelliteId) { if (nextSatelliteId) {
await selectSatellite(nextSatelliteId); await selectSatellite(nextSatelliteId);
} else { } else {
state.selectedSatelliteId = null;
state.details = null; state.details = null;
renderSummary(null); renderSummary(null);
renderTable(null); renderTable(null);
} }
} catch (error) { } catch (error) {
state.satellites = []; state.satellites = [];
state.selectedSatelliteId = null;
state.details = null; state.details = null;
renderList(); renderList();
renderSummary(null); renderSummary(null);
@@ -389,6 +389,41 @@ class CatalogControllerTest {
assertEquals(42L, requestCaptor.value.ic.orbPoint.revolution) 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 @Test
fun `satellite create endpoint proxies request`() { fun `satellite create endpoint proxies request`() {
val request = SatelliteCreateDTO( val request = SatelliteCreateDTO(
@@ -59,6 +59,7 @@ class BallisticsServiceTest {
) )
) )
) )
val currentInitialConditions = service.currentInitialConditions(101L)
assertEquals( assertEquals(
listOf( listOf(
@@ -67,7 +68,8 @@ class BallisticsServiceTest {
"/api/satellites/101/orbit", "/api/satellites/101/orbit",
"/api/satellites/orbit/availability", "/api/satellites/orbit/availability",
"/api/satellites/101/extract-time", "/api/satellites/101/extract-time",
"/api/satellites/receive-rv" "/api/satellites/receive-rv",
"/api/satellites/101/initial-conditions/current"
), ),
requests.map { it.path } requests.map { it.path }
) )
@@ -78,6 +80,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, null,
null,
null null
), ),
requests.map { it.query } requests.map { it.query }
@@ -87,6 +90,8 @@ class BallisticsServiceTest {
assertNotNull(exactPoint) assertNotNull(exactPoint)
assertEquals(timeStart, exactPoint.time) assertEquals(timeStart, exactPoint.time)
assertEquals(true, requestBodies.single().contains(""""movementModel":"KONDOR"""")) assertEquals(true, requestBodies.single().contains(""""movementModel":"KONDOR""""))
assertNotNull(currentInitialConditions)
assertEquals(101L, currentInitialConditions.satelliteId)
} }
private fun createService(serverUrl: String): BallisticsService { private fun createService(serverUrl: String): BallisticsService {
@@ -133,6 +138,32 @@ class BallisticsServiceTest {
requestBodies.add(exchange.requestBody.bufferedReader().use { it.readText() }) requestBodies.add(exchange.requestBody.bufferedReader().use { it.readText() })
respond(exchange, "") 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() startedServer.start()
server = startedServer server = startedServer
return "http://localhost:${startedServer.address.port}" return "http://localhost:${startedServer.address.port}"