Формат времени в UTC
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ class TLEStepper(str1: String, str2: String, earthType: EarthType) : AbstractSte
|
|||||||
private val ascendingNodeCache = mutableMapOf<Int, Double>()
|
private val ascendingNodeCache = mutableMapOf<Int, Double>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
baseEpoch = extractUTCMillis(satellite) / 1000.0 + 10800
|
baseEpoch = extractUTCMillis(satellite) / 1000.0
|
||||||
nextAscendingNodeAfterEpoch = findNextAscendingNodeAfterEpoch()
|
nextAscendingNodeAfterEpoch = findNextAscendingNodeAfterEpoch()
|
||||||
ascendingNodeCache[0] = nextAscendingNodeAfterEpoch
|
ascendingNodeCache[0] = nextAscendingNodeAfterEpoch
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ class AstronomerJ2000(var earthType: EarthType) {
|
|||||||
var d = tDtTm / 86400.0
|
var d = tDtTm / 86400.0
|
||||||
var dat = truncate(d)
|
var dat = truncate(d)
|
||||||
var tt = d - dat
|
var tt = d - dat
|
||||||
if (tt < 0.125) {
|
// if (tt < 0.125) {
|
||||||
dat--
|
// dat--
|
||||||
}
|
// }
|
||||||
|
|
||||||
var t = (dat - baseDate) / (36525.0)
|
var t = (dat - baseDate) / (36525.0)
|
||||||
var t2 = t * t
|
var t2 = t * t
|
||||||
@@ -69,11 +69,11 @@ class AstronomerJ2000(var earthType: EarthType) {
|
|||||||
|
|
||||||
psi += r
|
psi += r
|
||||||
|
|
||||||
if (tt < 0.125) {
|
// if (tt < 0.125) {
|
||||||
tt += 0.875
|
// tt += 0.875
|
||||||
} else {
|
// } else {
|
||||||
tt -= 0.125
|
// tt -= 0.125
|
||||||
}
|
// }
|
||||||
|
|
||||||
r = 84381.448e0 - 46.84024e0 * t - 0.00059e0 * t2 + 0.001813e0 * t3
|
r = 84381.448e0 - 46.84024e0 * t - 0.00059e0 * t2 + 0.001813e0 * t3
|
||||||
r = r * PI / 180.0 / 3600.0
|
r = r * PI / 180.0 / 3600.0
|
||||||
@@ -172,7 +172,7 @@ class AstronomerJ2000(var earthType: EarthType) {
|
|||||||
|
|
||||||
fun moonCoordinates(tm: Double): Vector3D {
|
fun moonCoordinates(tm: Double): Vector3D {
|
||||||
var d: Double = tm / 86400.0
|
var d: Double = tm / 86400.0
|
||||||
d = d - 0.125
|
// d = d - 0.125
|
||||||
val t: Double = (d - baseDate) / 36525.0 // Юлианский период ot 12h 1.1.2000
|
val t: Double = (d - baseDate) / 36525.0 // Юлианский период ot 12h 1.1.2000
|
||||||
val t2 = t * t
|
val t2 = t * t
|
||||||
val t3 = t2 * t
|
val t3 = t2 * t
|
||||||
@@ -241,8 +241,8 @@ class AstronomerJ2000(var earthType: EarthType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sunCoordinates(tm: Double): Vector3D {
|
fun sunCoordinates(tm: Double): Vector3D {
|
||||||
var d = tm / 86400.0
|
var dd = tm / 86400.0
|
||||||
var dd = d - 0.125
|
// var dd = d - 0.125
|
||||||
var t = (dd - baseDate) / 36525.0
|
var t = (dd - baseDate) / 36525.0
|
||||||
var t2 = t * t
|
var t2 = t * t
|
||||||
var t3 = t2 * t
|
var t3 = t2 * t
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ fun main() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
val tt2 = LocalDateTime.of(2028, 12, 11, 1, 24, 24, 257000000)
|
val tt2 = LocalDateTime.of(2028, 12, 10, 22, 24, 24, 257000000)
|
||||||
|
|
||||||
val op = OrbitalPoint(
|
val op = OrbitalPoint(
|
||||||
fromDateTime(tt2),
|
fromDateTime(tt2),
|
||||||
|
|||||||
+2
-1
@@ -2,11 +2,12 @@ package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
import jakarta.validation.constraints.DecimalMin
|
import jakarta.validation.constraints.DecimalMin
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Schema(description = "Запрос на расчет параметров выхода на заданное время")
|
@Schema(description = "Запрос на расчет параметров выхода на заданное время")
|
||||||
class ExactTimePositionRequestDTO(
|
class ExactTimePositionRequestDTO(
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
val timeStop : LocalDateTime? = null,
|
val timeStop : LocalDateTime? = null,
|
||||||
@field:DecimalMin(value = "1",
|
@field:DecimalMin(value = "1",
|
||||||
message = "Шаг не может быть меньше 1 мс")
|
message = "Шаг не может быть меньше 1 мс")
|
||||||
|
|||||||
+2
-1
@@ -2,6 +2,7 @@ package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Schema(description = "Модель данных параметров трассы полета и полосы обзора")
|
@Schema(description = "Модель данных параметров трассы полета и полосы обзора")
|
||||||
@@ -12,7 +13,7 @@ class FlightLineDTO(
|
|||||||
required = true,
|
required = true,
|
||||||
nullable = false
|
nullable = false
|
||||||
)
|
)
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@field:Schema(
|
@field:Schema(
|
||||||
description = "Виток"
|
description = "Виток"
|
||||||
|
|||||||
+3
-2
@@ -2,6 +2,7 @@ package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import jakarta.validation.constraints.NotEmpty
|
import jakarta.validation.constraints.NotEmpty
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class IcRvaRequestDTO(
|
class IcRvaRequestDTO(
|
||||||
@@ -13,8 +14,8 @@ class IcRvaRequestDTO(
|
|||||||
@field:NotEmpty(message = "Массив станций приема")
|
@field:NotEmpty(message = "Массив станций приема")
|
||||||
val station: StationDTO = StationDTO(),
|
val station: StationDTO = StationDTO(),
|
||||||
|
|
||||||
val timeStart : LocalDateTime = LocalDateTime.now(),
|
val timeStart : LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
val timeStop : LocalDateTime = LocalDateTime.now()
|
val timeStop : LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
+3
-2
@@ -1,10 +1,11 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class IntervalDTO(
|
class IntervalDTO(
|
||||||
val begin : LocalDateTime = LocalDateTime.now(),
|
val begin : LocalDateTime = UtcDateTimes.now(),
|
||||||
val end : LocalDateTime = LocalDateTime.now(),
|
val end : LocalDateTime = UtcDateTimes.now(),
|
||||||
val durationSec : Double = 0.0
|
val durationSec : Double = 0.0
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,11 +1,12 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class ObjViewRequestDTO(
|
class ObjViewRequestDTO(
|
||||||
val satellites : List<Long> = listOf(),
|
val satellites : List<Long> = listOf(),
|
||||||
val timeStart : LocalDateTime = LocalDateTime.now(),
|
val timeStart : LocalDateTime = UtcDateTimes.now(),
|
||||||
val timeStop : LocalDateTime = LocalDateTime.now(),
|
val timeStop : LocalDateTime = UtcDateTimes.now(),
|
||||||
val obj : ObjDTO = ObjDTO(),
|
val obj : ObjDTO = ObjDTO(),
|
||||||
val sunAngleMin : Double? = null,
|
val sunAngleMin : Double? = null,
|
||||||
val rollMin : Double? = null,
|
val rollMin : Double? = null,
|
||||||
|
|||||||
+2
-1
@@ -1,9 +1,10 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class OrbPointDTO(
|
class OrbPointDTO(
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
val revolution : Long = 0,
|
val revolution : Long = 0,
|
||||||
val vx : Double = 0.0,
|
val vx : Double = 0.0,
|
||||||
val vy : Double = 0.0,
|
val vy : Double = 0.0,
|
||||||
|
|||||||
+2
-1
@@ -1,11 +1,12 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class PointViewParamDTO(
|
class PointViewParamDTO(
|
||||||
val noradId : Long = 0,
|
val noradId : Long = 0,
|
||||||
val objectId : String = "",
|
val objectId : String = "",
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
val revolution : Long = 0,
|
val revolution : Long = 0,
|
||||||
val gamma : Double = 0.0,
|
val gamma : Double = 0.0,
|
||||||
val sunAngle : Double = 0.0,
|
val sunAngle : Double = 0.0,
|
||||||
|
|||||||
+3
-2
@@ -1,10 +1,11 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class SatelliteOrbitAvailabilityDTO(
|
class SatelliteOrbitAvailabilityDTO(
|
||||||
val satelliteId: Long = 0,
|
val satelliteId: Long = 0,
|
||||||
val timeStart: LocalDateTime = LocalDateTime.now(),
|
val timeStart: LocalDateTime = UtcDateTimes.now(),
|
||||||
val timeStop: LocalDateTime = LocalDateTime.now()
|
val timeStop: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
|
||||||
@@ -8,10 +9,10 @@ class SquareViewParamDTO(
|
|||||||
val noradId : Long = 0,
|
val noradId : Long = 0,
|
||||||
val objectId : String = "",
|
val objectId : String = "",
|
||||||
val revolutionBegin : Long = 0,
|
val revolutionBegin : Long = 0,
|
||||||
val timeBegin : LocalDateTime = LocalDateTime.now(),
|
val timeBegin : LocalDateTime = UtcDateTimes.now(),
|
||||||
val revSignBegin : RevolutionSign = RevolutionSign.ASC,
|
val revSignBegin : RevolutionSign = RevolutionSign.ASC,
|
||||||
val revolutionEnd: Long = 0,
|
val revolutionEnd: Long = 0,
|
||||||
val timeEnd : LocalDateTime = LocalDateTime.now(),
|
val timeEnd : LocalDateTime = UtcDateTimes.now(),
|
||||||
val revSignEnd : RevolutionSign = RevolutionSign.ASC,
|
val revSignEnd : RevolutionSign = RevolutionSign.ASC,
|
||||||
val gammaMin : Double = 0.0,
|
val gammaMin : Double = 0.0,
|
||||||
val gammaMax : Double = 0.0
|
val gammaMax : Double = 0.0
|
||||||
|
|||||||
+2
-1
@@ -1,12 +1,13 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "Модель данных целеуказаний")
|
@Schema(description = "Модель данных целеуказаний")
|
||||||
class TargetPositionDTO(
|
class TargetPositionDTO(
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
val elevation : Double = 0.0,
|
val elevation : Double = 0.0,
|
||||||
val azimuth : Double = 0.0,
|
val azimuth : Double = 0.0,
|
||||||
val range : Double = 0.0
|
val range : Double = 0.0
|
||||||
|
|||||||
+2
-1
@@ -1,8 +1,9 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class TlePointRequestDTO(
|
class TlePointRequestDTO(
|
||||||
var tle: TLEDTO = TLEDTO(),
|
var tle: TLEDTO = TLEDTO(),
|
||||||
var time: LocalDateTime = LocalDateTime.now()
|
var time: LocalDateTime = UtcDateTimes.now()
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-2
@@ -2,6 +2,7 @@ package space.nstart.pcp.pcp_types_lib.dto.ballistics
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import jakarta.validation.constraints.NotEmpty
|
import jakarta.validation.constraints.NotEmpty
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class TleRvaRequestDTO(
|
class TleRvaRequestDTO(
|
||||||
@@ -11,9 +12,9 @@ class TleRvaRequestDTO(
|
|||||||
@field:NotEmpty(message = "Массив станций приема")
|
@field:NotEmpty(message = "Массив станций приема")
|
||||||
val station: StationDTO = StationDTO(),
|
val station: StationDTO = StationDTO(),
|
||||||
|
|
||||||
val timeStart : LocalDateTime = LocalDateTime.now(),
|
val timeStart : LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
val timeStop : LocalDateTime = LocalDateTime.now()
|
val timeStop : LocalDateTime = UtcDateTimes.now()
|
||||||
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
+3
-2
@@ -1,11 +1,12 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.coverage
|
package space.nstart.pcp.pcp_types_lib.dto.coverage
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class CoverageCandidateDebugDTO(
|
class CoverageCandidateDebugDTO(
|
||||||
val satelliteId: Long = 0,
|
val satelliteId: Long = 0,
|
||||||
val timeStart: LocalDateTime = LocalDateTime.now(),
|
val timeStart: LocalDateTime = UtcDateTimes.now(),
|
||||||
val timeEnd: LocalDateTime = LocalDateTime.now(),
|
val timeEnd: LocalDateTime = UtcDateTimes.now(),
|
||||||
val rollDegrees: Double = 0.0,
|
val rollDegrees: Double = 0.0,
|
||||||
val accepted: Boolean = false,
|
val accepted: Boolean = false,
|
||||||
val reason: String = "",
|
val reason: String = "",
|
||||||
|
|||||||
+3
-2
@@ -1,12 +1,13 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.coverage
|
package space.nstart.pcp.pcp_types_lib.dto.coverage
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class CoverageSchemeCalculateRequestDTO(
|
class CoverageSchemeCalculateRequestDTO(
|
||||||
val polygonWkt: String = "",
|
val polygonWkt: String = "",
|
||||||
val satelliteIds: List<Long> = listOf(),
|
val satelliteIds: List<Long> = listOf(),
|
||||||
val timeStart: LocalDateTime = LocalDateTime.now(),
|
val timeStart: LocalDateTime = UtcDateTimes.now(),
|
||||||
val timeEnd: LocalDateTime = LocalDateTime.now(),
|
val timeEnd: LocalDateTime = UtcDateTimes.now(),
|
||||||
val rollStepDegrees: Double? = null,
|
val rollStepDegrees: Double? = null,
|
||||||
val minimumTechnologyOverlap: Double? = null,
|
val minimumTechnologyOverlap: Double? = null,
|
||||||
val strictContinuousCoverage: Boolean = true,
|
val strictContinuousCoverage: Boolean = true,
|
||||||
|
|||||||
+3
-2
@@ -1,10 +1,11 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.requests
|
package space.nstart.pcp.pcp_types_lib.dto.requests
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class EarthCellCalculationRequestDTO(
|
class EarthCellCalculationRequestDTO(
|
||||||
val timeStart : LocalDateTime = LocalDateTime.now(),
|
val timeStart : LocalDateTime = UtcDateTimes.now(),
|
||||||
val timeStop : LocalDateTime = LocalDateTime.now(),
|
val timeStop : LocalDateTime = UtcDateTimes.now(),
|
||||||
val cell : EarthCellWithRequestsDTO = EarthCellWithRequestsDTO()
|
val cell : EarthCellWithRequestsDTO = EarthCellWithRequestsDTO()
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
+2
-1
@@ -1,12 +1,13 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.requests.slots
|
package space.nstart.pcp.pcp_types_lib.dto.requests.slots
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
data class BookedSlotsStatusChangedEvent(
|
data class BookedSlotsStatusChangedEvent(
|
||||||
val bookedSlotIds: List<Long> = emptyList(),
|
val bookedSlotIds: List<Long> = emptyList(),
|
||||||
val status: BookedSlotStatus = BookedSlotStatus.BOOKED,
|
val status: BookedSlotStatus = BookedSlotStatus.BOOKED,
|
||||||
val occurredAt: LocalDateTime = LocalDateTime.now(),
|
val occurredAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
val sourceService: String = "",
|
val sourceService: String = "",
|
||||||
val missionId: UUID? = null,
|
val missionId: UUID? = null,
|
||||||
val modeId: Long? = null,
|
val modeId: Long? = null,
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.satellite.mission
|
package space.nstart.pcp.pcp_types_lib.dto.satellite.mission
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,10 +24,10 @@ data class DropRouteSaveRequestDTO(
|
|||||||
val revolution: Long = 0,
|
val revolution: Long = 0,
|
||||||
|
|
||||||
@field:Schema(description = "Начало зоны радиовидимости (локальное время без зоны)")
|
@field:Schema(description = "Начало зоны радиовидимости (локальное время без зоны)")
|
||||||
val zoneStart: LocalDateTime = LocalDateTime.now(),
|
val zoneStart: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@field:Schema(description = "Конец зоны радиовидимости (локальное время без зоны)")
|
@field:Schema(description = "Конец зоны радиовидимости (локальное время без зоны)")
|
||||||
val zoneStop: LocalDateTime = LocalDateTime.now(),
|
val zoneStop: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@field:Schema(description = "Идентификаторы режимов съёмки (survey_modes), которые сбрасываются в этой зоне")
|
@field:Schema(description = "Идентификаторы режимов съёмки (survey_modes), которые сбрасываются в этой зоне")
|
||||||
val surveyModeIds: List<Long> = emptyList(),
|
val surveyModeIds: List<Long> = emptyList(),
|
||||||
|
|||||||
+2
-1
@@ -3,6 +3,7 @@ package space.nstart.pcp.pcp_types_lib.dto.satellite.mission
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
import jakarta.validation.constraints.DecimalMin
|
import jakarta.validation.constraints.DecimalMin
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,7 +23,7 @@ data class SurveyRouteBuildRequestDTO(
|
|||||||
val satelliteId: Long = 0,
|
val satelliteId: Long = 0,
|
||||||
|
|
||||||
@field:Schema(description = "Момент видимости (локальное время без зоны)")
|
@field:Schema(description = "Момент видимости (локальное время без зоны)")
|
||||||
val time: LocalDateTime = LocalDateTime.now(),
|
val time: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@field:DecimalMin(value = "1", message = "Длительность съёмки должна быть не меньше 1 секунды")
|
@field:DecimalMin(value = "1", message = "Длительность съёмки должна быть не меньше 1 секунды")
|
||||||
@field:Schema(description = "Длительность съёмки, секунды")
|
@field:Schema(description = "Длительность съёмки, секунды")
|
||||||
|
|||||||
+3
-2
@@ -1,11 +1,12 @@
|
|||||||
package space.nstart.pcp.pcp_types_lib.dto.satellite.modes
|
package space.nstart.pcp.pcp_types_lib.dto.satellite.modes
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
data class SurveyModeInfoDTO(
|
data class SurveyModeInfoDTO(
|
||||||
val revolution : Long = 0,
|
val revolution : Long = 0,
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
val timStop : LocalDateTime = LocalDateTime.now(),
|
val timStop : LocalDateTime = UtcDateTimes.now(),
|
||||||
val duration : Double = 0.0,
|
val duration : Double = 0.0,
|
||||||
val roll : Double = 0.0,
|
val roll : Double = 0.0,
|
||||||
val contourWKT : String = "",
|
val contourWKT : String = "",
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat
|
|||||||
import com.fasterxml.jackson.annotation.JsonAlias
|
import com.fasterxml.jackson.annotation.JsonAlias
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||||
import space.nstart.pcp.pcp_types_lib.utils.json.LocalDateTimeTimeZoneSerializer
|
import space.nstart.pcp.pcp_types_lib.utils.json.LocalDateTimeTimeZoneSerializer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ class KafkaMessage<out T> (
|
|||||||
|
|
||||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS[xxx]")
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS[xxx]")
|
||||||
@JsonSerialize(using = LocalDateTimeTimeZoneSerializer::class)
|
@JsonSerialize(using = LocalDateTimeTimeZoneSerializer::class)
|
||||||
val time: LocalDateTime = LocalDateTime.now()
|
val time: LocalDateTime = UtcDateTimes.now()
|
||||||
|
|
||||||
val dataContentType = "application/json"
|
val dataContentType = "application/json"
|
||||||
val specVersion = "1.0"
|
val specVersion = "1.0"
|
||||||
|
|||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
package space.nstart.pcp.pcp_types_lib.utils
|
||||||
|
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.format.DateTimeParseException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared UTC helpers for services that still keep LocalDateTime in domain and DB models.
|
||||||
|
*
|
||||||
|
* LocalDateTime values are treated as UTC-naive: the stored object has no zone, but its
|
||||||
|
* numeric fields represent a UTC instant. Public API may receive either explicit UTC/offset
|
||||||
|
* ISO strings or legacy naive ISO strings; explicit offsets are normalized to UTC.
|
||||||
|
*/
|
||||||
|
object UtcDateTimes {
|
||||||
|
val UTC: ZoneOffset = ZoneOffset.UTC
|
||||||
|
|
||||||
|
fun now(clock: Clock = Clock.systemUTC()): LocalDateTime =
|
||||||
|
LocalDateTime.ofInstant(clock.instant(), UTC)
|
||||||
|
|
||||||
|
fun parse(value: String): LocalDateTime {
|
||||||
|
val normalized = normalize(value)
|
||||||
|
|
||||||
|
parseOffset(normalized)?.let { return it }
|
||||||
|
parseInstant(normalized)?.let { return it }
|
||||||
|
parseLocal(normalized)?.let { return it }
|
||||||
|
|
||||||
|
throw DateTimeParseException(
|
||||||
|
"Expected ISO-8601 UTC/offset date-time or legacy UTC-naive date-time",
|
||||||
|
value,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(value: LocalDateTime): String = value.toInstant(UTC).toString()
|
||||||
|
|
||||||
|
fun parseOrNull(value: String): LocalDateTime? = runCatching { parse(value) }.getOrNull()
|
||||||
|
|
||||||
|
private fun normalize(value: String): String {
|
||||||
|
val trimmed = value.trim().replace(Regex(" (\\d{2}:\\d{2})$"), "+\$1")
|
||||||
|
return if (trimmed.length == 16) "$trimmed:00" else trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseOffset(value: String): LocalDateTime? = runCatching {
|
||||||
|
OffsetDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
.withOffsetSameInstant(UTC)
|
||||||
|
.toLocalDateTime()
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
|
private fun parseInstant(value: String): LocalDateTime? = runCatching {
|
||||||
|
LocalDateTime.ofInstant(Instant.parse(value), UTC)
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
|
private fun parseLocal(value: String): LocalDateTime? = runCatching {
|
||||||
|
LocalDateTime.parse(value, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
+6
-10
@@ -3,20 +3,16 @@ package space.nstart.pcp.pcp_types_lib.utils.json
|
|||||||
import com.fasterxml.jackson.core.JsonGenerator
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
import com.fasterxml.jackson.databind.JsonSerializer
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
import com.fasterxml.jackson.databind.SerializerProvider
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneId
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
class LocalDateTimeTimeZoneSerializer: JsonSerializer<LocalDateTime?>() {
|
class LocalDateTimeTimeZoneSerializer: JsonSerializer<LocalDateTime?>() {
|
||||||
|
|
||||||
companion object {
|
|
||||||
val fmt: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSxxx")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun serialize(p0: LocalDateTime?, p1: JsonGenerator, p2: SerializerProvider) {
|
override fun serialize(p0: LocalDateTime?, p1: JsonGenerator, p2: SerializerProvider) {
|
||||||
|
if (p0 == null) {
|
||||||
//todo: проверить корректность зоны по нормальным правилам
|
p1.writeNull()
|
||||||
// p1.writeString(p0?.atZone(ZoneId.of("Europe/Moscow"))?.format(fmt))
|
} else {
|
||||||
p1.writeString(p0?.atZone(ZoneId.systemDefault())?.format(fmt))
|
p1.writeString(UtcDateTimes.format(p0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package space.nstart.pcp.pcp_types_lib.utils
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
class UtcDateTimesTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `normalizes offset date-time to utc naive LocalDateTime`() {
|
||||||
|
val actual = UtcDateTimes.parse("2026-06-17T09:28:27.334+03:00")
|
||||||
|
|
||||||
|
assertEquals(LocalDateTime.of(2026, 6, 17, 6, 28, 27, 334_000_000), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `treats legacy naive date-time as utc naive LocalDateTime`() {
|
||||||
|
val actual = UtcDateTimes.parse("2026-06-17T06:28:27.334")
|
||||||
|
|
||||||
|
assertEquals(LocalDateTime.of(2026, 6, 17, 6, 28, 27, 334_000_000), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `formats utc naive LocalDateTime with Z suffix`() {
|
||||||
|
val actual = UtcDateTimes.format(LocalDateTime.of(2026, 6, 17, 6, 28, 27, 334_000_000))
|
||||||
|
|
||||||
|
assertEquals("2026-06-17T06:28:27.334Z", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-7
@@ -8,10 +8,12 @@ import org.springframework.web.bind.annotation.RequestMapping
|
|||||||
import org.springframework.web.bind.annotation.RequestParam
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
import space.nstart.pcp.pcp_request_service.configuration.CustomValidationException
|
import space.nstart.pcp.pcp_request_service.configuration.CustomValidationException
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.AngularMotionCalculationRequestDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.AngularMotionCalculationRequestDTO
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeParseException
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/angular-motion")
|
@RequestMapping("/api/angular-motion")
|
||||||
@@ -43,12 +45,12 @@ class AngularMotionController(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val decoded = URLDecoder.decode(raw, StandardCharsets.UTF_8)
|
val decoded = URLDecoder.decode(raw, StandardCharsets.UTF_8)
|
||||||
val normalized = if (decoded.length == 16) "$decoded:00" else decoded
|
return try {
|
||||||
return runCatching { LocalDateTime.parse(normalized) }
|
UtcDateTimes.parse(decoded)
|
||||||
.getOrElse {
|
} catch (_: DateTimeParseException) {
|
||||||
throw CustomValidationException(
|
throw CustomValidationException(
|
||||||
"Некорректный формат времени '$decoded'. Ожидается yyyy-MM-dd'T'HH:mm или yyyy-MM-dd'T'HH:mm:ss",
|
"Некорректный формат времени '$decoded'. Ожидается ISO-8601 UTC/offset, например 2026-06-17T06:28:27Z, или legacy UTC-naive yyyy-MM-dd'T'HH:mm:ss",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package space.nstart.pcp.pcp_request_service.configuration
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import java.time.Clock
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class TimeConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun clock(): Clock = Clock.systemUTC()
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package space.nstart.pcp.pcp_request_service.configuration
|
||||||
|
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeJsonConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun ballisticsUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-ballistics-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
-14
@@ -15,9 +15,11 @@ import org.springframework.web.bind.annotation.RestController
|
|||||||
import space.nstart.pcp.pcp_request_service.configuration.CustomValidationException
|
import space.nstart.pcp.pcp_request_service.configuration.CustomValidationException
|
||||||
import space.nstart.pcp.pcp_request_service.service.SatelliteIcEventService
|
import space.nstart.pcp.pcp_request_service.service.SatelliteIcEventService
|
||||||
import space.nstart.pcp.pcp_request_service.service.SatelliteService
|
import space.nstart.pcp.pcp_request_service.service.SatelliteService
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeParseException
|
||||||
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -61,11 +63,11 @@ class SatelliteController {
|
|||||||
@Parameter(description = "Идентификатор NORAD", example = "56756")
|
@Parameter(description = "Идентификатор NORAD", example = "56756")
|
||||||
@PathVariable("norad_id") noradId : Long,
|
@PathVariable("norad_id") noradId : Long,
|
||||||
@Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
|
@Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
|
||||||
@RequestParam("time_start") timeStart : LocalDateTime?,
|
@RequestParam("time_start", required = false) timeStart : String?,
|
||||||
@Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
|
@Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
|
||||||
@RequestParam("time_stop") timeStrop : LocalDateTime?
|
@RequestParam("time_stop", required = false) timeStrop : String?
|
||||||
) =
|
) =
|
||||||
satelliteService.getOrbPoints(noradId, timeStart, timeStrop)
|
satelliteService.getOrbPoints(noradId, parseOptionalUtcDateTime("time_start", timeStart), parseOptionalUtcDateTime("time_stop", timeStrop))
|
||||||
|
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Получить доступные интервалы прогноза по всем КА",
|
summary = "Получить доступные интервалы прогноза по всем КА",
|
||||||
@@ -74,9 +76,9 @@ class SatelliteController {
|
|||||||
@GetMapping("/orbit/availability")
|
@GetMapping("/orbit/availability")
|
||||||
fun getOrbitAvailability(
|
fun getOrbitAvailability(
|
||||||
@Parameter(description = "Проверяемое время. Если null, возвращаются все КА, для которых есть точки ПДЦМ")
|
@Parameter(description = "Проверяемое время. Если null, возвращаются все КА, для которых есть точки ПДЦМ")
|
||||||
@RequestParam("time", required = false) time: LocalDateTime?
|
@RequestParam("time", required = false) time: String?
|
||||||
) =
|
) =
|
||||||
satelliteService.getOrbitAvailability(time)
|
satelliteService.getOrbitAvailability(parseOptionalUtcDateTime("time", time))
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/orbit/availability/{id}")
|
@GetMapping("/orbit/availability/{id}")
|
||||||
@@ -96,11 +98,11 @@ class SatelliteController {
|
|||||||
@Parameter(description = "Идентификатор NORAD", example = "56756")
|
@Parameter(description = "Идентификатор NORAD", example = "56756")
|
||||||
@PathVariable("norad_id") noradId : Long,
|
@PathVariable("norad_id") noradId : Long,
|
||||||
@Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
|
@Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
|
||||||
@RequestParam("time_start") timeStart : LocalDateTime?,
|
@RequestParam("time_start", required = false) timeStart : String?,
|
||||||
@Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
|
@Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
|
||||||
@RequestParam("time_stop") timeStrop : LocalDateTime?
|
@RequestParam("time_stop", required = false) timeStrop : String?
|
||||||
) =
|
) =
|
||||||
satelliteService.getAscNodes(noradId, timeStart, timeStrop)
|
satelliteService.getAscNodes(noradId, parseOptionalUtcDateTime("time_start", timeStart), parseOptionalUtcDateTime("time_stop", timeStrop))
|
||||||
|
|
||||||
|
|
||||||
@Operation(
|
@Operation(
|
||||||
@@ -112,11 +114,11 @@ class SatelliteController {
|
|||||||
@Parameter(description = "Идентификатор NORAD", example = "56756")
|
@Parameter(description = "Идентификатор NORAD", example = "56756")
|
||||||
@PathVariable("norad_id") noradId : Long,
|
@PathVariable("norad_id") noradId : Long,
|
||||||
@Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
|
@Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
|
||||||
@RequestParam("time_start") timeStart : LocalDateTime?,
|
@RequestParam("time_start", required = false) timeStart : String?,
|
||||||
@Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
|
@Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
|
||||||
@RequestParam("time_stop") timeStrop : LocalDateTime?
|
@RequestParam("time_stop", required = false) timeStrop : String?
|
||||||
) =
|
) =
|
||||||
satelliteService.getFL(noradId, timeStart, timeStrop, 60.0)
|
satelliteService.getFL(noradId, parseOptionalUtcDateTime("time_start", timeStart), parseOptionalUtcDateTime("time_stop", timeStrop), 60.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -130,11 +132,11 @@ class SatelliteController {
|
|||||||
@Parameter(description = "Идентификатор NORAD", example = "56756")
|
@Parameter(description = "Идентификатор NORAD", example = "56756")
|
||||||
@PathVariable("norad_id") noradId : Long,
|
@PathVariable("norad_id") noradId : Long,
|
||||||
@Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
|
@Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
|
||||||
@RequestParam("time_start") timeStart : LocalDateTime,
|
@RequestParam("time_start") timeStart : String,
|
||||||
@Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
|
@Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
|
||||||
@RequestParam("time_stop") timeStrop : LocalDateTime
|
@RequestParam("time_stop") timeStrop : String
|
||||||
) =
|
) =
|
||||||
satelliteService.getRVA(noradId, timeStart, timeStrop)
|
satelliteService.getRVA(noradId, parseRequiredUtcDateTime("time_start", timeStart), parseRequiredUtcDateTime("time_stop", timeStrop))
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/{satellite_id}/clear")
|
@PostMapping("/{satellite_id}/clear")
|
||||||
@@ -143,4 +145,16 @@ class SatelliteController {
|
|||||||
) =
|
) =
|
||||||
satelliteService.clear(satelliteId)
|
satelliteService.clear(satelliteId)
|
||||||
|
|
||||||
|
private fun parseOptionalUtcDateTime(name: String, value: String?): LocalDateTime? =
|
||||||
|
value?.let { parseRequiredUtcDateTime(name, it) }
|
||||||
|
|
||||||
|
private fun parseRequiredUtcDateTime(name: String, value: String): LocalDateTime =
|
||||||
|
try {
|
||||||
|
UtcDateTimes.parse(value)
|
||||||
|
} catch (_: DateTimeParseException) {
|
||||||
|
throw CustomValidationException(
|
||||||
|
"Некорректный формат параметра '$name': '$value'. Ожидается ISO-8601 UTC/offset, например 2026-06-17T06:28:27Z, или legacy UTC-naive yyyy-MM-dd'T'HH:mm:ss",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package space.nstart.pcp.pcp_request_service.entity
|
package space.nstart.pcp.pcp_request_service.entity
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import ballistics.types.KeplerParams
|
import ballistics.types.KeplerParams
|
||||||
import ballistics.types.RevolutionParameter
|
import ballistics.types.RevolutionParameter
|
||||||
import ballistics.utils.fromDateTime
|
import ballistics.utils.fromDateTime
|
||||||
@@ -26,7 +27,7 @@ class AscNodeEntity(
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val satelliteId : Long = 0,
|
val satelliteId : Long = 0,
|
||||||
@Column(name = "t", nullable = false)
|
@Column(name = "t", nullable = false)
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val revolution : Long = 0,
|
val revolution : Long = 0,
|
||||||
val longitude : Double = 0.0,
|
val longitude : Double = 0.0,
|
||||||
|
|||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
package space.nstart.pcp.pcp_request_service.entity
|
package space.nstart.pcp.pcp_request_service.entity
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import ballistics.utils.toDateTime
|
import ballistics.utils.toDateTime
|
||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
import jakarta.persistence.Entity
|
import jakarta.persistence.Entity
|
||||||
@@ -26,13 +27,13 @@ class EarthCellViewsEntity(
|
|||||||
@Column(name = "rev_begin", nullable = false)
|
@Column(name = "rev_begin", nullable = false)
|
||||||
val revolutionBegin : Long = 0,
|
val revolutionBegin : Long = 0,
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val timeBegin : LocalDateTime = LocalDateTime.now(),
|
val timeBegin : LocalDateTime = UtcDateTimes.now(),
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val revSignBegin : Int = 0,
|
val revSignBegin : Int = 0,
|
||||||
@Column(name = "rev_end", nullable = false)
|
@Column(name = "rev_end", nullable = false)
|
||||||
val revolutionEnd: Long = 0,
|
val revolutionEnd: Long = 0,
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val timeEnd : LocalDateTime = LocalDateTime.now(),
|
val timeEnd : LocalDateTime = UtcDateTimes.now(),
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val revSignEnd : Int = 0,
|
val revSignEnd : Int = 0,
|
||||||
@Column(name = "roll_min")
|
@Column(name = "roll_min")
|
||||||
|
|||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
package space.nstart.pcp.pcp_request_service.entity
|
package space.nstart.pcp.pcp_request_service.entity
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import ballistics.types.FleghtLineSector
|
import ballistics.types.FleghtLineSector
|
||||||
import ballistics.utils.toDateTime
|
import ballistics.utils.toDateTime
|
||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
@@ -22,9 +23,9 @@ class EarthCoverageEntity(
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val cellNumber : Long = 0,
|
val cellNumber : Long = 0,
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val tStart : LocalDateTime = LocalDateTime.now(),
|
val tStart : LocalDateTime = UtcDateTimes.now(),
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val tStop : LocalDateTime = LocalDateTime.now(),
|
val tStop : LocalDateTime = UtcDateTimes.now(),
|
||||||
) {
|
) {
|
||||||
constructor(fl : FleghtLineSector, sector : Int, sat : Long) : this(
|
constructor(fl : FleghtLineSector, sector : Int, sat : Long) : this(
|
||||||
null,
|
null,
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package space.nstart.pcp.pcp_request_service.entity
|
package space.nstart.pcp.pcp_request_service.entity
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import ballistics.types.FlightLine
|
import ballistics.types.FlightLine
|
||||||
import ballistics.utils.toDateTime
|
import ballistics.utils.toDateTime
|
||||||
import jakarta.persistence.*
|
import jakarta.persistence.*
|
||||||
@@ -19,7 +20,7 @@ class FlightLineEntity(
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val satelliteId : Long = 0,
|
val satelliteId : Long = 0,
|
||||||
@Column(name = "t", nullable = false)
|
@Column(name = "t", nullable = false)
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val revolution : Long = 0,
|
val revolution : Long = 0,
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package space.nstart.pcp.pcp_request_service.entity
|
package space.nstart.pcp.pcp_request_service.entity
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import ballistics.types.OrbitalPoint
|
import ballistics.types.OrbitalPoint
|
||||||
import ballistics.utils.fromDateTime
|
import ballistics.utils.fromDateTime
|
||||||
import ballistics.utils.math.Vector3D
|
import ballistics.utils.math.Vector3D
|
||||||
@@ -18,7 +19,7 @@ class PDCMEntity (
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val satelliteId : Long = 0,
|
val satelliteId : Long = 0,
|
||||||
@Column(name = "t", nullable = false)
|
@Column(name = "t", nullable = false)
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val revolution : Long = 0,
|
val revolution : Long = 0,
|
||||||
val x: Double = 0.0,
|
val x: Double = 0.0,
|
||||||
|
|||||||
+7
-5
@@ -1,5 +1,6 @@
|
|||||||
package space.nstart.pcp.pcp_request_service.entity
|
package space.nstart.pcp.pcp_request_service.entity
|
||||||
|
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
import jakarta.persistence.Entity
|
import jakarta.persistence.Entity
|
||||||
import jakarta.persistence.EnumType
|
import jakarta.persistence.EnumType
|
||||||
@@ -27,7 +28,7 @@ class SatelliteInitialConditionEntity(
|
|||||||
@Column(name = "movement_model", nullable = false)
|
@Column(name = "movement_model", nullable = false)
|
||||||
val movementModel: MovementModel = MovementModel.FOTO,
|
val movementModel: MovementModel = MovementModel.FOTO,
|
||||||
@Column(name = "orb_time", nullable = false)
|
@Column(name = "orb_time", nullable = false)
|
||||||
val orbTime: LocalDateTime = LocalDateTime.now(),
|
val orbTime: LocalDateTime = UtcDateTimes.now(),
|
||||||
@Column(name = "orb_revolution", nullable = false)
|
@Column(name = "orb_revolution", nullable = false)
|
||||||
val orbRevolution: Long = 0,
|
val orbRevolution: Long = 0,
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@@ -47,13 +48,13 @@ class SatelliteInitialConditionEntity(
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
val f81: Double = 147.8,
|
val f81: Double = 147.8,
|
||||||
@Column(name = "received_at", nullable = false)
|
@Column(name = "received_at", nullable = false)
|
||||||
val receivedAt: LocalDateTime = LocalDateTime.now(),
|
val receivedAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
@Column(name = "calculated_at")
|
@Column(name = "calculated_at")
|
||||||
var calculatedAt: LocalDateTime? = null,
|
var calculatedAt: LocalDateTime? = null,
|
||||||
@Column(name = "last_calculated", nullable = false)
|
@Column(name = "last_calculated", nullable = false)
|
||||||
var lastCalculated: Boolean = false
|
var lastCalculated: Boolean = false
|
||||||
) {
|
) {
|
||||||
constructor(request: SatelliteICDTO) : this(
|
constructor(request: SatelliteICDTO, receivedAt: LocalDateTime = UtcDateTimes.now()) : this(
|
||||||
satelliteId = request.satelliteId,
|
satelliteId = request.satelliteId,
|
||||||
movementModel = request.movementModel,
|
movementModel = request.movementModel,
|
||||||
orbTime = request.ic.orbPoint.time,
|
orbTime = request.ic.orbPoint.time,
|
||||||
@@ -65,10 +66,11 @@ class SatelliteInitialConditionEntity(
|
|||||||
vy = request.ic.orbPoint.vy,
|
vy = request.ic.orbPoint.vy,
|
||||||
vz = request.ic.orbPoint.vz,
|
vz = request.ic.orbPoint.vz,
|
||||||
sBall = request.ic.sBall,
|
sBall = request.ic.sBall,
|
||||||
f81 = request.ic.f81
|
f81 = request.ic.f81,
|
||||||
|
receivedAt = receivedAt
|
||||||
)
|
)
|
||||||
|
|
||||||
fun markLastCalculated(calculatedAt: LocalDateTime = LocalDateTime.now()) {
|
fun markLastCalculated(calculatedAt: LocalDateTime = UtcDateTimes.now()) {
|
||||||
this.calculatedAt = calculatedAt
|
this.calculatedAt = calculatedAt
|
||||||
this.lastCalculated = true
|
this.lastCalculated = true
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -1,6 +1,8 @@
|
|||||||
package space.nstart.pcp.pcp_request_service.service
|
package space.nstart.pcp.pcp_request_service.service
|
||||||
|
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
|
import java.time.Clock
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
import space.nstart.pcp.pcp_request_service.entity.SatelliteInitialConditionEntity
|
import space.nstart.pcp.pcp_request_service.entity.SatelliteInitialConditionEntity
|
||||||
import space.nstart.pcp.pcp_request_service.repository.SatelliteInitialConditionRepository
|
import space.nstart.pcp.pcp_request_service.repository.SatelliteInitialConditionRepository
|
||||||
@@ -8,17 +10,18 @@ import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
class SatelliteInitialConditionService(
|
class SatelliteInitialConditionService(
|
||||||
private val satelliteInitialConditionRepository: SatelliteInitialConditionRepository
|
private val satelliteInitialConditionRepository: SatelliteInitialConditionRepository,
|
||||||
|
private val clock: Clock
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun saveReceived(request: SatelliteICDTO): SatelliteInitialConditionEntity =
|
fun saveReceived(request: SatelliteICDTO): SatelliteInitialConditionEntity =
|
||||||
satelliteInitialConditionRepository.save(SatelliteInitialConditionEntity(request))
|
satelliteInitialConditionRepository.save(SatelliteInitialConditionEntity(request, UtcDateTimes.now(clock)))
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun markLastCalculated(initialCondition: SatelliteInitialConditionEntity) {
|
fun markLastCalculated(initialCondition: SatelliteInitialConditionEntity) {
|
||||||
satelliteInitialConditionRepository.clearLastCalculated(initialCondition.satelliteId)
|
satelliteInitialConditionRepository.clearLastCalculated(initialCondition.satelliteId)
|
||||||
initialCondition.markLastCalculated()
|
initialCondition.markLastCalculated(UtcDateTimes.now(clock))
|
||||||
satelliteInitialConditionRepository.save(initialCondition)
|
satelliteInitialConditionRepository.save(initialCondition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -44,6 +44,7 @@ import space.nstart.pcp.pcp_request_service.repository.EarthCoverageRepository
|
|||||||
import space.nstart.pcp.pcp_request_service.repository.FlightLineRepository
|
import space.nstart.pcp.pcp_request_service.repository.FlightLineRepository
|
||||||
import space.nstart.pcp.pcp_request_service.repository.PDCMRepository
|
import space.nstart.pcp.pcp_request_service.repository.PDCMRepository
|
||||||
import space.nstart.pcp.pcp_request_service.utils.ContourClipService
|
import space.nstart.pcp.pcp_request_service.utils.ContourClipService
|
||||||
|
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.AscNodeDTO
|
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.ExactTimePositionRequestDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO
|
||||||
@@ -62,6 +63,7 @@ import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteOrbitAvailabilityD
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TLEDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TLEDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TargetPositionDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TargetPositionDTO
|
||||||
import tools.jackson.databind.ObjectMapper
|
import tools.jackson.databind.ObjectMapper
|
||||||
|
import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import kotlin.collections.forEach
|
import kotlin.collections.forEach
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
@@ -105,6 +107,8 @@ class SatelliteService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private lateinit var stationCatalogClient: StationCatalogClient
|
private lateinit var stationCatalogClient: StationCatalogClient
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private lateinit var clock: Clock
|
||||||
|
|
||||||
val astro = AstronomerJ2000(EarthType.PZ90d02)
|
val astro = AstronomerJ2000(EarthType.PZ90d02)
|
||||||
|
|
||||||
@@ -761,8 +765,8 @@ class SatelliteService {
|
|||||||
fun getOrbPoints(id : Long, tn : LocalDateTime?, tk : LocalDateTime?) =
|
fun getOrbPoints(id : Long, tn : LocalDateTime?, tk : LocalDateTime?) =
|
||||||
pdcmRepository.findBySatelliteIdAndTimeBetween(
|
pdcmRepository.findBySatelliteIdAndTimeBetween(
|
||||||
id,
|
id,
|
||||||
tn?: LocalDateTime.now(),
|
tn?: UtcDateTimes.now(clock),
|
||||||
tk?: LocalDateTime.now().plusDays(1))
|
tk?: UtcDateTimes.now(clock).plusDays(1))
|
||||||
.map { it.toDTO()
|
.map { it.toDTO()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,8 +784,8 @@ class SatelliteService {
|
|||||||
|
|
||||||
fun getFL(id : Long, tn : LocalDateTime?, tk : LocalDateTime?, step : Double?) =
|
fun getFL(id : Long, tn : LocalDateTime?, tk : LocalDateTime?, step : Double?) =
|
||||||
flightLineRepository.findBySatelliteIdAndTimeBetween(id,
|
flightLineRepository.findBySatelliteIdAndTimeBetween(id,
|
||||||
tn?: LocalDateTime.now(),
|
tn?: UtcDateTimes.now(clock),
|
||||||
tk?: LocalDateTime.now().plusDays(1)
|
tk?: UtcDateTimes.now(clock).plusDays(1)
|
||||||
)
|
)
|
||||||
.map { it.toDTO() }
|
.map { it.toDTO() }
|
||||||
|
|
||||||
@@ -902,8 +906,8 @@ class SatelliteService {
|
|||||||
|
|
||||||
return ascNodeRepository.findBySatelliteIdAndTimeBetween(
|
return ascNodeRepository.findBySatelliteIdAndTimeBetween(
|
||||||
id,
|
id,
|
||||||
tn ?: LocalDateTime.now(),
|
tn ?: UtcDateTimes.now(clock),
|
||||||
tk ?: LocalDateTime.now().plusDays(1)
|
tk ?: UtcDateTimes.now(clock).plusDays(1)
|
||||||
)
|
)
|
||||||
.map {
|
.map {
|
||||||
val keps = calculateKeplerParams(
|
val keps = calculateKeplerParams(
|
||||||
|
|||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
package space.nstart.pcp.pcp_request_service.utils
|
||||||
|
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.format.DateTimeParseException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTC boundary helpers for pcp-ballistics-service.
|
||||||
|
*
|
||||||
|
* Domain and database still use LocalDateTime, but every value is treated as an
|
||||||
|
* absolute UTC instant without a stored zone (UTC-naive). Public API values may
|
||||||
|
* be sent either as UTC/offset ISO strings or as legacy naive ISO strings;
|
||||||
|
* offset values are normalized to UTC LocalDateTime.
|
||||||
|
*/
|
||||||
|
object UtcDateTimes {
|
||||||
|
val UTC: ZoneOffset = ZoneOffset.UTC
|
||||||
|
|
||||||
|
fun now(clock: Clock = Clock.systemUTC()): LocalDateTime =
|
||||||
|
LocalDateTime.ofInstant(clock.instant(), UTC)
|
||||||
|
|
||||||
|
fun parse(value: String): LocalDateTime {
|
||||||
|
val normalized = normalize(value)
|
||||||
|
|
||||||
|
parseOffset(normalized)?.let { return it }
|
||||||
|
parseInstant(normalized)?.let { return it }
|
||||||
|
parseLocal(normalized)?.let { return it }
|
||||||
|
|
||||||
|
throw DateTimeParseException(
|
||||||
|
"Expected ISO-8601 UTC/offset date-time or legacy UTC-naive date-time",
|
||||||
|
value,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(value: LocalDateTime): String = value.toInstant(UTC).toString()
|
||||||
|
|
||||||
|
private fun normalize(value: String): String {
|
||||||
|
val trimmed = value.trim().replace(Regex(" (\\d{2}:\\d{2})$"), "+\$1")
|
||||||
|
return if (trimmed.length == 16) "$trimmed:00" else trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseOffset(value: String): LocalDateTime? = runCatching {
|
||||||
|
OffsetDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
.withOffsetSameInstant(UTC)
|
||||||
|
.toLocalDateTime()
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
|
private fun parseInstant(value: String): LocalDateTime? = runCatching {
|
||||||
|
LocalDateTime.ofInstant(Instant.parse(value), UTC)
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
|
private fun parseLocal(value: String): LocalDateTime? = runCatching {
|
||||||
|
LocalDateTime.parse(value, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package space.nstart.pcp.pcp_request_service.utils
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
class UtcDateTimesTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `parses utc instant with z suffix`() {
|
||||||
|
assertEquals(
|
||||||
|
LocalDateTime.of(2026, 6, 17, 6, 28, 27, 334_000_000),
|
||||||
|
UtcDateTimes.parse("2026-06-17T06:28:27.334Z")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `normalizes offset date time to utc naive local date time`() {
|
||||||
|
assertEquals(
|
||||||
|
LocalDateTime.of(2026, 6, 17, 6, 28, 27),
|
||||||
|
UtcDateTimes.parse("2026-06-17T09:28:27+03:00")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `keeps legacy naive date time as utc naive`() {
|
||||||
|
assertEquals(
|
||||||
|
LocalDateTime.of(2026, 6, 17, 6, 28, 0),
|
||||||
|
UtcDateTimes.parse("2026-06-17T06:28")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `formats utc naive date time with z suffix`() {
|
||||||
|
assertEquals(
|
||||||
|
"2026-06-17T06:28:27.334Z",
|
||||||
|
UtcDateTimes.format(LocalDateTime.of(2026, 6, 17, 6, 28, 27, 334_000_000))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
package space.nstart.pcp.pcp_satellites_service.configuration
|
||||||
|
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.format.FormatterRegistry
|
||||||
|
import org.springframework.web.reactive.config.WebFluxConfigurer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeJsonConfiguration : WebFluxConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun complexMissionUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-complex-mission-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFormatters(registry: FormatterRegistry) {
|
||||||
|
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
|
||||||
|
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-3
@@ -12,6 +12,7 @@ import jakarta.persistence.Id
|
|||||||
import jakarta.persistence.Index
|
import jakarta.persistence.Index
|
||||||
import jakarta.persistence.OneToMany
|
import jakarta.persistence.OneToMany
|
||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@@ -34,7 +35,7 @@ class ComplexPlanRunEntity(
|
|||||||
var status: ComplexPlanRunStatus = ComplexPlanRunStatus.CREATED,
|
var status: ComplexPlanRunStatus = ComplexPlanRunStatus.CREATED,
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
var createdAt: LocalDateTime = LocalDateTime.now(),
|
var createdAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "started_at")
|
@Column(name = "started_at")
|
||||||
var startedAt: LocalDateTime? = null,
|
var startedAt: LocalDateTime? = null,
|
||||||
@@ -46,10 +47,10 @@ class ComplexPlanRunEntity(
|
|||||||
var durationMs: Long? = null,
|
var durationMs: Long? = null,
|
||||||
|
|
||||||
@Column(name = "interval_start", nullable = false)
|
@Column(name = "interval_start", nullable = false)
|
||||||
var intervalStart: LocalDateTime = LocalDateTime.now(),
|
var intervalStart: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "interval_end", nullable = false)
|
@Column(name = "interval_end", nullable = false)
|
||||||
var intervalEnd: LocalDateTime = LocalDateTime.now(),
|
var intervalEnd: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "requested_by")
|
@Column(name = "requested_by")
|
||||||
var requestedBy: String? = null,
|
var requestedBy: String? = null,
|
||||||
|
|||||||
+3
-2
@@ -17,6 +17,7 @@ import jakarta.persistence.PreUpdate
|
|||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import jakarta.persistence.Index
|
import jakarta.persistence.Index
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.ModeDataRates
|
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.ModeDataRates
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@@ -46,10 +47,10 @@ class SatelliteModeEntity(
|
|||||||
var type: SatelliteModeType = SatelliteModeType.SURVEY,
|
var type: SatelliteModeType = SatelliteModeType.SURVEY,
|
||||||
|
|
||||||
@Column(name = "start_time", nullable = false)
|
@Column(name = "start_time", nullable = false)
|
||||||
var startTime: LocalDateTime = LocalDateTime.now(),
|
var startTime: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "end_time", nullable = false)
|
@Column(name = "end_time", nullable = false)
|
||||||
var endTime: LocalDateTime = LocalDateTime.now(),
|
var endTime: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "revolution", nullable = false)
|
@Column(name = "revolution", nullable = false)
|
||||||
var revolution: Long = 0,
|
var revolution: Long = 0,
|
||||||
|
|||||||
+4
-3
@@ -12,6 +12,7 @@ import jakarta.persistence.JoinColumn
|
|||||||
import jakarta.persistence.ManyToOne
|
import jakarta.persistence.ManyToOne
|
||||||
import jakarta.persistence.OneToMany
|
import jakarta.persistence.OneToMany
|
||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@@ -34,13 +35,13 @@ class SatelliteModeSnapshotEntity(
|
|||||||
var satelliteId: Long = 0,
|
var satelliteId: Long = 0,
|
||||||
|
|
||||||
@Column(name = "interval_start", nullable = false)
|
@Column(name = "interval_start", nullable = false)
|
||||||
var intervalStart: LocalDateTime = LocalDateTime.now(),
|
var intervalStart: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "interval_end", nullable = false)
|
@Column(name = "interval_end", nullable = false)
|
||||||
var intervalEnd: LocalDateTime = LocalDateTime.now(),
|
var intervalEnd: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
var createdAt: LocalDateTime = LocalDateTime.now(),
|
var createdAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "is_active", nullable = false)
|
@Column(name = "is_active", nullable = false)
|
||||||
var isActive: Boolean = false,
|
var isActive: Boolean = false,
|
||||||
|
|||||||
+3
-2
@@ -2,12 +2,13 @@ package space.nstart.pcp.pcp_satellites_service.model.mode
|
|||||||
|
|
||||||
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeSource
|
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeSource
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.satellite.modes.SurveyModeInfoDTO
|
import space.nstart.pcp.pcp_types_lib.dto.satellite.modes.SurveyModeInfoDTO
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class SurveyMode(
|
class SurveyMode(
|
||||||
val revolution : Long = 1,
|
val revolution : Long = 1,
|
||||||
val time : LocalDateTime = LocalDateTime.now(),
|
val time : LocalDateTime = UtcDateTimes.now(),
|
||||||
val timeStop : LocalDateTime = LocalDateTime.now(),
|
val timeStop : LocalDateTime = UtcDateTimes.now(),
|
||||||
val roll : Double = 0.0,
|
val roll : Double = 0.0,
|
||||||
val latitude : Double = 0.0,
|
val latitude : Double = 0.0,
|
||||||
val longitude : Double = 0.0,
|
val longitude : Double = 0.0,
|
||||||
|
|||||||
+7
-6
@@ -14,6 +14,7 @@ import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookingRequestDTO
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.SlotDTO
|
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.SlotDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.SurveySlotDTO
|
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.SurveySlotDTO
|
||||||
import tools.jackson.databind.ObjectMapper
|
import tools.jackson.databind.ObjectMapper
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -34,8 +35,8 @@ class SlotService(webClientBuilderProvider: ObjectProvider<WebClient.Builder>) {
|
|||||||
UriComponentsBuilder
|
UriComponentsBuilder
|
||||||
.fromUriString("/api/slots/poly-cover")
|
.fromUriString("/api/slots/poly-cover")
|
||||||
.queryParam("wkt", wkt)
|
.queryParam("wkt", wkt)
|
||||||
.queryParam("timeStart", tn)
|
.queryParam("timeStart", UtcDateTimes.format(tn))
|
||||||
.queryParam("timeStop", tk)
|
.queryParam("timeStop", UtcDateTimes.format(tk))
|
||||||
.queryParam("cov", cov)
|
.queryParam("cov", cov)
|
||||||
.apply {
|
.apply {
|
||||||
sats.forEach { queryParam("satellites", it) }
|
sats.forEach { queryParam("satellites", it) }
|
||||||
@@ -49,8 +50,8 @@ class SlotService(webClientBuilderProvider: ObjectProvider<WebClient.Builder>) {
|
|||||||
.uri(
|
.uri(
|
||||||
"/api/slots/poly-cover?wkt={wkt}&timeStart={timeStart}&timeStop={timeStop}&revSign={revSign}&cov={cov}",
|
"/api/slots/poly-cover?wkt={wkt}&timeStart={timeStart}&timeStop={timeStop}&revSign={revSign}&cov={cov}",
|
||||||
wkt,
|
wkt,
|
||||||
tn,
|
UtcDateTimes.format(tn),
|
||||||
tk,
|
UtcDateTimes.format(tk),
|
||||||
sign,
|
sign,
|
||||||
cov
|
cov
|
||||||
)
|
)
|
||||||
@@ -71,8 +72,8 @@ class SlotService(webClientBuilderProvider: ObjectProvider<WebClient.Builder>) {
|
|||||||
.uri(
|
.uri(
|
||||||
"/api/slots/mission/satellite?satellite_id={satelliteId}&timeStart={timeStart}&timeStop={timeStop}",
|
"/api/slots/mission/satellite?satellite_id={satelliteId}&timeStart={timeStart}&timeStop={timeStop}",
|
||||||
id,
|
id,
|
||||||
tn,
|
UtcDateTimes.format(tn),
|
||||||
tk
|
UtcDateTimes.format(tk)
|
||||||
)
|
)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.onStatus({ status -> status.isError }) { response ->
|
.onStatus({ status -> status.isError }) { response ->
|
||||||
|
|||||||
+3
-2
@@ -16,6 +16,7 @@ import space.nstart.pcp.pcp_types_lib.dto.ballistics.ObjViewRequestDTO
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.PointViewParamDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.PointViewParamDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamDTO
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@@ -82,8 +83,8 @@ class BallisticsClient(
|
|||||||
.uri { uriBuilder ->
|
.uri { uriBuilder ->
|
||||||
uriBuilder
|
uriBuilder
|
||||||
.path("/api/satellites/{satelliteId}/flight-line")
|
.path("/api/satellites/{satelliteId}/flight-line")
|
||||||
.queryParam("time_start", timeStart)
|
.queryParam("time_start", UtcDateTimes.format(timeStart))
|
||||||
.queryParam("time_stop", timeStop)
|
.queryParam("time_stop", UtcDateTimes.format(timeStop))
|
||||||
.build(satelliteId)
|
.build(satelliteId)
|
||||||
}
|
}
|
||||||
.retrieve()
|
.retrieve()
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package space.nstart.pcp.pcp_coverage_scheme_service.configuration
|
||||||
|
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.format.FormatterRegistry
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeConfiguration : WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun clock(): Clock = Clock.systemUTC()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun coverageSchemeUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-coverage-scheme-service-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFormatters(registry: FormatterRegistry) {
|
||||||
|
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
|
||||||
|
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -1,11 +1,21 @@
|
|||||||
package space.nstart.pcp.complan
|
package space.nstart.pcp.complan
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature
|
import com.fasterxml.jackson.databind.SerializationFeature
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||||
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
|
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
class JacksonConfig {
|
class JacksonConfig {
|
||||||
|
|
||||||
@@ -13,8 +23,26 @@ class JacksonConfig {
|
|||||||
fun objectMapper(): ObjectMapper {
|
fun objectMapper(): ObjectMapper {
|
||||||
val mapper = ObjectMapper()
|
val mapper = ObjectMapper()
|
||||||
mapper.registerModule(JavaTimeModule())
|
mapper.registerModule(JavaTimeModule())
|
||||||
|
.registerModule(utcLocalDateTimeModule())
|
||||||
.registerKotlinModule()
|
.registerKotlinModule()
|
||||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||||
return mapper
|
return mapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun utcLocalDateTimeModule(): SimpleModule = SimpleModule("pcp-dynamic-plan-service-jackson2-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeDeserializer())
|
||||||
|
|
||||||
|
private class Jackson2UtcLocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Jackson2UtcLocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -58,6 +58,7 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
private const val INTERVAL_COVERAGE_AREA_TOLERANCE = 1.0e-9
|
private const val INTERVAL_COVERAGE_AREA_TOLERANCE = 1.0e-9
|
||||||
private const val INTERVAL_ROUTE_WIDTH_RATIO = 0.9
|
private const val INTERVAL_ROUTE_WIDTH_RATIO = 0.9
|
||||||
@@ -841,7 +842,7 @@ class BasicPlanCalculator(
|
|||||||
val resultFilePath = if (request.saveResultToFile && status == ComplexPlanCalculationStatus.COMPLETED) {
|
val resultFilePath = if (request.saveResultToFile && status == ComplexPlanCalculationStatus.COMPLETED) {
|
||||||
observationParametersDebugFileWriter.write(
|
observationParametersDebugFileWriter.write(
|
||||||
ObservationParametersDebugFileDTO(
|
ObservationParametersDebugFileDTO(
|
||||||
createdAt = LocalDateTime.now(),
|
createdAt = UtcDateTimes.now(),
|
||||||
requestId = request.requestId,
|
requestId = request.requestId,
|
||||||
satelliteIds = selectedSatelliteIds,
|
satelliteIds = selectedSatelliteIds,
|
||||||
missingSatelliteIds = missingSatelliteIds,
|
missingSatelliteIds = missingSatelliteIds,
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package space.nstart.pcp.complan.config
|
||||||
|
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.format.FormatterRegistry
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeConfiguration : WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun clock(): Clock = Clock.systemUTC()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun dynamicPlanUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-dynamic-plan-service-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFormatters(registry: FormatterRegistry) {
|
||||||
|
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
|
||||||
|
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -11,6 +11,7 @@ import jakarta.persistence.ManyToOne
|
|||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
@Table(name = "dynamic_plan_route")
|
@Table(name = "dynamic_plan_route")
|
||||||
@Entity
|
@Entity
|
||||||
@@ -31,10 +32,10 @@ class DynamicPlanRouteEntity(
|
|||||||
var satelliteId: Long = 0,
|
var satelliteId: Long = 0,
|
||||||
|
|
||||||
@Column(name = "start_time", nullable = false)
|
@Column(name = "start_time", nullable = false)
|
||||||
var startTime: LocalDateTime = LocalDateTime.now(),
|
var startTime: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "end_time", nullable = false)
|
@Column(name = "end_time", nullable = false)
|
||||||
var endTime: LocalDateTime = LocalDateTime.now(),
|
var endTime: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "duration", nullable = false)
|
@Column(name = "duration", nullable = false)
|
||||||
var duration: Double = 0.0,
|
var duration: Double = 0.0,
|
||||||
|
|||||||
+2
-1
@@ -11,6 +11,7 @@ import space.nstart.pcp.complan.types.DynamicPlanRevolutionMode
|
|||||||
import space.nstart.pcp.complan.types.DynamicPlanRunStatus
|
import space.nstart.pcp.complan.types.DynamicPlanRunStatus
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
@Table(name = "dynamic_plan_run")
|
@Table(name = "dynamic_plan_run")
|
||||||
@Entity
|
@Entity
|
||||||
@@ -47,7 +48,7 @@ class DynamicPlanRunEntity(
|
|||||||
var filterCoveredRoutes: Boolean = false,
|
var filterCoveredRoutes: Boolean = false,
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
var createdAt: LocalDateTime = LocalDateTime.now(),
|
var createdAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "started_at")
|
@Column(name = "started_at")
|
||||||
var startedAt: LocalDateTime? = null,
|
var startedAt: LocalDateTime? = null,
|
||||||
|
|||||||
+17
-11
@@ -10,10 +10,10 @@ import space.nstart.pcp.complan.types.ObjViewRequestDTO
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.FlightLineDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.FlightLineDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ObjDTO as BallisticsObjDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ObjDTO as BallisticsObjDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ObjViewRequestDTO as BallisticsObjViewRequestDTO
|
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteOrbitAvailabilityDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteOrbitAvailabilityDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamDTO
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**Получение данных от сервиса баллистики*/
|
/**Получение данных от сервиса баллистики*/
|
||||||
@@ -39,7 +39,7 @@ class BallTaskServce {
|
|||||||
UriComponentsBuilder
|
UriComponentsBuilder
|
||||||
.fromUriString(ballisticsServiceUrl)
|
.fromUriString(ballisticsServiceUrl)
|
||||||
.path("/api/satellites/orbit/availability")
|
.path("/api/satellites/orbit/availability")
|
||||||
.apply { time?.let { queryParam("time", it) } }
|
.apply { time?.let { queryParam("time", UtcDateTimes.format(it)) } }
|
||||||
.toUriString()
|
.toUriString()
|
||||||
)
|
)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
@@ -53,8 +53,8 @@ class BallTaskServce {
|
|||||||
UriComponentsBuilder
|
UriComponentsBuilder
|
||||||
.fromUriString(ballisticsServiceUrl)
|
.fromUriString(ballisticsServiceUrl)
|
||||||
.path("/api/satellites/$noradId/flight-line")
|
.path("/api/satellites/$noradId/flight-line")
|
||||||
.queryParam("time_start", timeStart)
|
.queryParam("time_start", UtcDateTimes.format(timeStart))
|
||||||
.queryParam("time_stop", timeStop)
|
.queryParam("time_stop", UtcDateTimes.format(timeStop))
|
||||||
.toUriString()
|
.toUriString()
|
||||||
)
|
)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
@@ -65,7 +65,13 @@ class BallTaskServce {
|
|||||||
RestClient.create()
|
RestClient.create()
|
||||||
.post()
|
.post()
|
||||||
.uri(buildBallisticsUri("/api/satellites/$noradId/extract-time"))
|
.uri(buildBallisticsUri("/api/satellites/$noradId/extract-time"))
|
||||||
.body(request)
|
.body(
|
||||||
|
mapOf(
|
||||||
|
"time" to UtcDateTimes.format(request.time),
|
||||||
|
"timeStop" to request.timeStop?.let { UtcDateTimes.format(it) },
|
||||||
|
"stepMs" to request.stepMs
|
||||||
|
)
|
||||||
|
)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.body(ORB_POINT_LIST_TYPE)
|
.body(ORB_POINT_LIST_TYPE)
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
@@ -80,16 +86,16 @@ class BallTaskServce {
|
|||||||
.post()
|
.post()
|
||||||
.uri(buildBallisticsUri("/api/obj-view/mpl-square"))
|
.uri(buildBallisticsUri("/api/obj-view/mpl-square"))
|
||||||
.body(
|
.body(
|
||||||
BallisticsObjViewRequestDTO(
|
mapOf(
|
||||||
satellites = satellites,
|
"satellites" to satellites,
|
||||||
timeStart = timeStart,
|
"timeStart" to UtcDateTimes.format(timeStart),
|
||||||
timeStop = timeStop,
|
"timeStop" to UtcDateTimes.format(timeStop),
|
||||||
obj = BallisticsObjDTO(
|
"obj" to BallisticsObjDTO(
|
||||||
id = obj.id.toString(),
|
id = obj.id.toString(),
|
||||||
position = obj.position,
|
position = obj.position,
|
||||||
contourWKT = obj.contourWKT
|
contourWKT = obj.contourWKT
|
||||||
),
|
),
|
||||||
sunAngleMin = objects.sunAngleMin
|
"sunAngleMin" to objects.sunAngleMin
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
|
|||||||
+6
-5
@@ -28,6 +28,7 @@ import java.time.LocalDateTime
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class DynamicPlanRunService(
|
class DynamicPlanRunService(
|
||||||
@@ -59,7 +60,7 @@ class DynamicPlanRunService(
|
|||||||
calculationMode = request.calculationMode,
|
calculationMode = request.calculationMode,
|
||||||
revolutionMode = request.revolutionMode,
|
revolutionMode = request.revolutionMode,
|
||||||
filterCoveredRoutes = request.filterCoveredRoutes,
|
filterCoveredRoutes = request.filterCoveredRoutes,
|
||||||
createdAt = LocalDateTime.now()
|
createdAt = UtcDateTimes.now()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to create calculation run")
|
} ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to create calculation run")
|
||||||
@@ -135,7 +136,7 @@ class DynamicPlanRunService(
|
|||||||
return@execute 0
|
return@execute 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val finishedAt = LocalDateTime.now()
|
val finishedAt = UtcDateTimes.now()
|
||||||
runs.forEach { run ->
|
runs.forEach { run ->
|
||||||
run.status = DynamicPlanRunStatus.FAILED
|
run.status = DynamicPlanRunStatus.FAILED
|
||||||
run.finishedAt = finishedAt
|
run.finishedAt = finishedAt
|
||||||
@@ -171,7 +172,7 @@ class DynamicPlanRunService(
|
|||||||
transactionTemplate.execute {
|
transactionTemplate.execute {
|
||||||
val run = runRepository.findById(runId).orElse(null) ?: return@execute null
|
val run = runRepository.findById(runId).orElse(null) ?: return@execute null
|
||||||
run.status = DynamicPlanRunStatus.RUNNING
|
run.status = DynamicPlanRunStatus.RUNNING
|
||||||
run.startedAt = LocalDateTime.now()
|
run.startedAt = UtcDateTimes.now()
|
||||||
run.errorMessage = null
|
run.errorMessage = null
|
||||||
runRepository.save(run)
|
runRepository.save(run)
|
||||||
run.toRequest()
|
run.toRequest()
|
||||||
@@ -182,7 +183,7 @@ class DynamicPlanRunService(
|
|||||||
val run = runRepository.findById(runId).orElseThrow()
|
val run = runRepository.findById(runId).orElseThrow()
|
||||||
val result = output.result
|
val result = output.result
|
||||||
run.status = DynamicPlanRunStatus.COMPLETED
|
run.status = DynamicPlanRunStatus.COMPLETED
|
||||||
run.finishedAt = LocalDateTime.now()
|
run.finishedAt = UtcDateTimes.now()
|
||||||
run.durationMs = result.durationMs
|
run.durationMs = result.durationMs
|
||||||
run.resultStatus = result.status.name
|
run.resultStatus = result.status.name
|
||||||
run.routesCount = result.routesCount
|
run.routesCount = result.routesCount
|
||||||
@@ -200,7 +201,7 @@ class DynamicPlanRunService(
|
|||||||
transactionTemplate.execute {
|
transactionTemplate.execute {
|
||||||
val run = runRepository.findById(runId).orElseThrow()
|
val run = runRepository.findById(runId).orElseThrow()
|
||||||
run.status = DynamicPlanRunStatus.FAILED
|
run.status = DynamicPlanRunStatus.FAILED
|
||||||
run.finishedAt = LocalDateTime.now()
|
run.finishedAt = UtcDateTimes.now()
|
||||||
run.errorMessage = error.message ?: error::class.java.simpleName
|
run.errorMessage = error.message ?: error::class.java.simpleName
|
||||||
runRepository.save(run)
|
runRepository.save(run)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -6,6 +6,7 @@ import org.locationtech.jts.geom.LineString
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.FlightLineDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.FlightLineDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.RevolutionSign
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.RevolutionSign
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Класс космического аппарата**
|
* Класс космического аппарата**
|
||||||
@@ -17,9 +18,9 @@ class SpaceCraft (
|
|||||||
val flightLine : MutableList<FlightLineDTO> = mutableListOf()
|
val flightLine : MutableList<FlightLineDTO> = mutableListOf()
|
||||||
|
|
||||||
/**Время начала планирования аппарата*/
|
/**Время начала планирования аппарата*/
|
||||||
var timeStart: LocalDateTime = LocalDateTime.now()
|
var timeStart: LocalDateTime = UtcDateTimes.now()
|
||||||
/**Время конца планирования аппарата*/
|
/**Время конца планирования аппарата*/
|
||||||
var timeStop : LocalDateTime = LocalDateTime.now()
|
var timeStop : LocalDateTime = UtcDateTimes.now()
|
||||||
/**
|
/**
|
||||||
* Создание линий полета восходящего и нисходящего витка
|
* Создание линий полета восходящего и нисходящего витка
|
||||||
* @param geomFactory фабрика геометрии
|
* @param geomFactory фабрика геометрии
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package space.nstart.pcp.pcp_mission_planing_service.configuration
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class Jackson2UtcConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun jackson2ObjectMapper(): ObjectMapper = jacksonObjectMapper()
|
||||||
|
.findAndRegisterModules()
|
||||||
|
.registerModule(utcLocalDateTimeModule())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun utcLocalDateTimeModule(): SimpleModule = SimpleModule("pcp-mission-planing-service-jackson2-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeDeserializer())
|
||||||
|
|
||||||
|
private class Jackson2UtcLocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Jackson2UtcLocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package space.nstart.pcp.pcp_mission_planing_service.configuration
|
||||||
|
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.format.FormatterRegistry
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeConfiguration : WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun clock(): Clock = Clock.systemUTC()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun missionPlaningUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-mission-planing-service-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFormatters(registry: FormatterRegistry) {
|
||||||
|
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
|
||||||
|
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -13,6 +13,7 @@ import space.nstart.pcp.pcp_types_lib.dto.ballistics.PointViewParamDTO
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.RadioVisibilityAreaDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.RadioVisibilityAreaDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamsWithPointsDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamsWithPointsDTO
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
|
||||||
@@ -55,8 +56,8 @@ class BallisticsService(webClientBuilderProvider: ObjectProvider<WebClient.Build
|
|||||||
.get()
|
.get()
|
||||||
.uri("/api/satellites/{id}/rva?time_start={time_start}&time_stop={time_stop}",
|
.uri("/api/satellites/{id}/rva?time_start={time_start}&time_stop={time_stop}",
|
||||||
id,
|
id,
|
||||||
tn,
|
UtcDateTimes.format(tn),
|
||||||
tk)
|
UtcDateTimes.format(tk))
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.bodyToFlux<RadioVisibilityAreaDTO>()
|
.bodyToFlux<RadioVisibilityAreaDTO>()
|
||||||
.toIterable()
|
.toIterable()
|
||||||
|
|||||||
+5
-3
@@ -13,8 +13,9 @@ import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotStatus
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotsStatusChangedEvent
|
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotsStatusChangedEvent
|
||||||
import space.nstart.pcp.pcp_types_lib.message.KafkaMessage
|
import space.nstart.pcp.pcp_types_lib.message.KafkaMessage
|
||||||
import space.nstart.pcp.pcp_types_lib.message.PcpKafkaEvent
|
import space.nstart.pcp.pcp_types_lib.message.PcpKafkaEvent
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.time.LocalDateTime
|
import java.time.Clock
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@@ -26,7 +27,8 @@ class BookedSlotsEventPublisher(
|
|||||||
kafkaTemplateProvider: ObjectProvider<KafkaTemplate<String, Any>>,
|
kafkaTemplateProvider: ObjectProvider<KafkaTemplate<String, Any>>,
|
||||||
objectMapperProvider: ObjectProvider<ObjectMapper>,
|
objectMapperProvider: ObjectProvider<ObjectMapper>,
|
||||||
@Value("\${app.kafka.topics.booked-slots:pcp.booked-slots}") private val topic: String,
|
@Value("\${app.kafka.topics.booked-slots:pcp.booked-slots}") private val topic: String,
|
||||||
@Value("\${spring.application.name:pcp-mission-planing-service}") private val applicationName: String
|
@Value("\${spring.application.name:pcp-mission-planing-service}") private val applicationName: String,
|
||||||
|
private val clock: Clock = Clock.systemUTC()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||||
@@ -59,7 +61,7 @@ class BookedSlotsEventPublisher(
|
|||||||
val event = BookedSlotsStatusChangedEvent(
|
val event = BookedSlotsStatusChangedEvent(
|
||||||
bookedSlotIds = normalizedIds,
|
bookedSlotIds = normalizedIds,
|
||||||
status = status,
|
status = status,
|
||||||
occurredAt = LocalDateTime.now(),
|
occurredAt = UtcDateTimes.now(clock),
|
||||||
sourceService = applicationName,
|
sourceService = applicationName,
|
||||||
missionId = missionId,
|
missionId = missionId,
|
||||||
modeId = modeId,
|
modeId = modeId,
|
||||||
|
|||||||
+3
-2
@@ -10,6 +10,7 @@ import reactor.core.publisher.Mono
|
|||||||
import space.nstart.pcp.pcp_types_lib.configuration.CustomErrorException
|
import space.nstart.pcp.pcp_types_lib.configuration.CustomErrorException
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeResponseDTO
|
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeResponseDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeSnapshotResponseDTO
|
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeSnapshotResponseDTO
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import tools.jackson.databind.ObjectMapper
|
import tools.jackson.databind.ObjectMapper
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@@ -70,8 +71,8 @@ class ComplexMissionService(webClientBuilderProvider: ObjectProvider<WebClient.B
|
|||||||
.uri(
|
.uri(
|
||||||
"/api/satellites/snapshots/{snapshotId}/modes?intervalStart={intervalStart}&intervalEnd={intervalEnd}",
|
"/api/satellites/snapshots/{snapshotId}/modes?intervalStart={intervalStart}&intervalEnd={intervalEnd}",
|
||||||
snapshotId,
|
snapshotId,
|
||||||
intervalStart,
|
UtcDateTimes.format(intervalStart),
|
||||||
intervalEnd
|
UtcDateTimes.format(intervalEnd)
|
||||||
)
|
)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.onStatus({ status -> status.isError }) { response ->
|
.onStatus({ status -> status.isError }) { response ->
|
||||||
|
|||||||
+6
-3
@@ -60,6 +60,8 @@ import space.nstart.pcp.pcp_types_lib.dto.ballistics.ObjViewRequestDTO
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.PositionDTO
|
import space.nstart.pcp.pcp_types_lib.dto.ballistics.PositionDTO
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotStatus
|
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotStatus
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeResponseDTO
|
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeResponseDTO
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.Clock
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@@ -81,6 +83,7 @@ class MissionPlaningService(
|
|||||||
private val complexMissionService: ComplexMissionService,
|
private val complexMissionService: ComplexMissionService,
|
||||||
private val satelliteCatalogClient: SatelliteCatalogClient,
|
private val satelliteCatalogClient: SatelliteCatalogClient,
|
||||||
private val satelliteMissionTypeFactory: SatelliteMissionTypeFactory,
|
private val satelliteMissionTypeFactory: SatelliteMissionTypeFactory,
|
||||||
|
private val clock: Clock = Clock.systemUTC(),
|
||||||
private val logger: Logger = LoggerFactory.getLogger(MissionPlaningService::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(MissionPlaningService::class.java)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -432,7 +435,7 @@ class MissionPlaningService(
|
|||||||
overrideIntervalEnd: LocalDateTime? = null,
|
overrideIntervalEnd: LocalDateTime? = null,
|
||||||
comPlanSnapshotId: Long? = null
|
comPlanSnapshotId: Long? = null
|
||||||
): MissionSurveyCalculationResponseDTO {
|
): MissionSurveyCalculationResponseDTO {
|
||||||
val startedAt = LocalDateTime.now()
|
val startedAt = UtcDateTimes.now(clock)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Расчет плана съемки stage 5 по persisted snapshot этапа 4 для миссии {}: requestedComPlanSnapshotId={}",
|
"Расчет плана съемки stage 5 по persisted snapshot этапа 4 для миссии {}: requestedComPlanSnapshotId={}",
|
||||||
missionId,
|
missionId,
|
||||||
@@ -520,7 +523,7 @@ class MissionPlaningService(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (survs.isEmpty()) {
|
if (survs.isEmpty()) {
|
||||||
val finishedAt = LocalDateTime.now()
|
val finishedAt = UtcDateTimes.now(clock)
|
||||||
return MissionSurveyCalculationResponseDTO(
|
return MissionSurveyCalculationResponseDTO(
|
||||||
missionId = missionId,
|
missionId = missionId,
|
||||||
completed = true,
|
completed = true,
|
||||||
@@ -565,7 +568,7 @@ class MissionPlaningService(
|
|||||||
modeBookings.size
|
modeBookings.size
|
||||||
)
|
)
|
||||||
|
|
||||||
val finishedAt = LocalDateTime.now()
|
val finishedAt = UtcDateTimes.now(clock)
|
||||||
return MissionSurveyCalculationResponseDTO(
|
return MissionSurveyCalculationResponseDTO(
|
||||||
missionId = missionId,
|
missionId = missionId,
|
||||||
completed = true,
|
completed = true,
|
||||||
|
|||||||
+27
@@ -1,10 +1,19 @@
|
|||||||
package org.nstart.dep265.requestservice.config
|
package org.nstart.dep265.requestservice.config
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
class JacksonConfig {
|
class JacksonConfig {
|
||||||
@@ -18,5 +27,23 @@ class JacksonConfig {
|
|||||||
return jacksonObjectMapper()
|
return jacksonObjectMapper()
|
||||||
.registerModule(JavaTimeModule())
|
.registerModule(JavaTimeModule())
|
||||||
.findAndRegisterModules()
|
.findAndRegisterModules()
|
||||||
|
.registerModule(utcLocalDateTimeModule())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun utcLocalDateTimeModule(): SimpleModule = SimpleModule("pcp-request-service-jackson2-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeDeserializer())
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Jackson2UtcLocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Jackson2UtcLocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package org.nstart.dep265.requestservice.config
|
||||||
|
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.format.FormatterRegistry
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeConfiguration : WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun clock(): Clock = Clock.systemUTC()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun requestServiceUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-request-service-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFormatters(registry: FormatterRegistry) {
|
||||||
|
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
|
||||||
|
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-9
@@ -12,7 +12,6 @@ import org.nstart.dep265.requestservice.dto.RequestStatusDto
|
|||||||
import org.nstart.dep265.requestservice.dto.RequestWithCellsResponseDto
|
import org.nstart.dep265.requestservice.dto.RequestWithCellsResponseDto
|
||||||
import org.nstart.dep265.requestservice.dto.SurveyTypeDto
|
import org.nstart.dep265.requestservice.dto.SurveyTypeDto
|
||||||
import org.nstart.dep265.requestservice.service.RequestService
|
import org.nstart.dep265.requestservice.service.RequestService
|
||||||
import org.springframework.format.annotation.DateTimeFormat
|
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping
|
import org.springframework.web.bind.annotation.DeleteMapping
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
@@ -41,10 +40,10 @@ class RequestController(
|
|||||||
@RequestParam(required = false) surveyType: SurveyTypeDto?,
|
@RequestParam(required = false) surveyType: SurveyTypeDto?,
|
||||||
@RequestParam(required = false) kpp: Int?,
|
@RequestParam(required = false) kpp: Int?,
|
||||||
@RequestParam(required = false) highPriorityTransmit: Boolean?,
|
@RequestParam(required = false) highPriorityTransmit: Boolean?,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginFrom: LocalDateTime?,
|
@RequestParam(required = false) beginFrom: LocalDateTime?,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginTo: LocalDateTime?,
|
@RequestParam(required = false) beginTo: LocalDateTime?,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endFrom: LocalDateTime?,
|
@RequestParam(required = false) endFrom: LocalDateTime?,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endTo: LocalDateTime?,
|
@RequestParam(required = false) endTo: LocalDateTime?,
|
||||||
@RequestParam(defaultValue = "false") includeDeleted: Boolean,
|
@RequestParam(defaultValue = "false") includeDeleted: Boolean,
|
||||||
@RequestParam(defaultValue = "0") page: Int,
|
@RequestParam(defaultValue = "0") page: Int,
|
||||||
@RequestParam(defaultValue = "50") size: Int,
|
@RequestParam(defaultValue = "50") size: Int,
|
||||||
@@ -73,10 +72,10 @@ class RequestController(
|
|||||||
@RequestParam(required = false) surveyType: SurveyTypeDto?,
|
@RequestParam(required = false) surveyType: SurveyTypeDto?,
|
||||||
@RequestParam(required = false) kpp: Int?,
|
@RequestParam(required = false) kpp: Int?,
|
||||||
@RequestParam(required = false) highPriorityTransmit: Boolean?,
|
@RequestParam(required = false) highPriorityTransmit: Boolean?,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginFrom: LocalDateTime?,
|
@RequestParam(required = false) beginFrom: LocalDateTime?,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginTo: LocalDateTime?,
|
@RequestParam(required = false) beginTo: LocalDateTime?,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endFrom: LocalDateTime?,
|
@RequestParam(required = false) endFrom: LocalDateTime?,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endTo: LocalDateTime?,
|
@RequestParam(required = false) endTo: LocalDateTime?,
|
||||||
@RequestParam(defaultValue = "false") includeDeleted: Boolean,
|
@RequestParam(defaultValue = "false") includeDeleted: Boolean,
|
||||||
@RequestParam(defaultValue = "0") page: Int,
|
@RequestParam(defaultValue = "0") page: Int,
|
||||||
@RequestParam(defaultValue = "50") size: Int,
|
@RequestParam(defaultValue = "50") size: Int,
|
||||||
|
|||||||
+4
-1
@@ -7,6 +7,8 @@ import org.nstart.dep265.requestservice.repository.RequestCellRepository
|
|||||||
import org.nstart.dep265.requestservice.repository.RequestRepository
|
import org.nstart.dep265.requestservice.repository.RequestRepository
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class GridRebuildAlreadyRunningException : RuntimeException("Grid rebuild is already running")
|
class GridRebuildAlreadyRunningException : RuntimeException("Grid rebuild is already running")
|
||||||
@@ -20,6 +22,7 @@ class GridRebuildService(
|
|||||||
private val requestGridProjectionService: RequestGridProjectionService,
|
private val requestGridProjectionService: RequestGridProjectionService,
|
||||||
private val earthCellRepository: EarthCellRepository,
|
private val earthCellRepository: EarthCellRepository,
|
||||||
private val gridRebuildLockService: GridRebuildLockService,
|
private val gridRebuildLockService: GridRebuildLockService,
|
||||||
|
private val clock: Clock = Clock.systemUTC(),
|
||||||
) {
|
) {
|
||||||
@Transactional
|
@Transactional
|
||||||
fun rebuildGrid(): GridRebuildResponseDto {
|
fun rebuildGrid(): GridRebuildResponseDto {
|
||||||
@@ -40,7 +43,7 @@ class GridRebuildService(
|
|||||||
return GridRebuildResponseDto(
|
return GridRebuildResponseDto(
|
||||||
cellsCount = earthCellRepository.count(),
|
cellsCount = earthCellRepository.count(),
|
||||||
rebuiltRequestProjectionsCount = activeRequests.size.toLong(),
|
rebuiltRequestProjectionsCount = activeRequests.size.toLong(),
|
||||||
rebuiltAt = LocalDateTime.now(),
|
rebuiltAt = UtcDateTimes.now(clock),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -11,6 +11,8 @@ import org.springframework.scheduling.annotation.Scheduled
|
|||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.support.TransactionCallback
|
import org.springframework.transaction.support.TransactionCallback
|
||||||
import org.springframework.transaction.support.TransactionOperations
|
import org.springframework.transaction.support.TransactionOperations
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.Clock
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
@@ -25,6 +27,7 @@ class OutboxPublisherService(
|
|||||||
|
|
||||||
@param:Qualifier("outboxTransactionOperations")
|
@param:Qualifier("outboxTransactionOperations")
|
||||||
private val transactionOperations: TransactionOperations,
|
private val transactionOperations: TransactionOperations,
|
||||||
|
private val clock: Clock = Clock.systemUTC(),
|
||||||
) {
|
) {
|
||||||
private val log = LoggerFactory.getLogger(this::class.java)
|
private val log = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
@@ -42,7 +45,7 @@ class OutboxPublisherService(
|
|||||||
return try {
|
return try {
|
||||||
transactionOperations.execute(
|
transactionOperations.execute(
|
||||||
TransactionCallback {
|
TransactionCallback {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now(clock)
|
||||||
val events = outboxEventRepository.findPublishableForUpdateSkipLocked(
|
val events = outboxEventRepository.findPublishableForUpdateSkipLocked(
|
||||||
eventType = RequestStatusOutboxService.REQUEST_STATUS_EVENT_TYPE,
|
eventType = RequestStatusOutboxService.REQUEST_STATUS_EVENT_TYPE,
|
||||||
now = now,
|
now = now,
|
||||||
|
|||||||
+4
-1
@@ -9,6 +9,8 @@ import org.springframework.scheduling.annotation.Scheduled
|
|||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.support.TransactionCallback
|
import org.springframework.transaction.support.TransactionCallback
|
||||||
import org.springframework.transaction.support.TransactionOperations
|
import org.springframework.transaction.support.TransactionOperations
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,6 +35,7 @@ class RequestExpiryService(
|
|||||||
|
|
||||||
@param:Qualifier("requestExpiryTransactionOperations")
|
@param:Qualifier("requestExpiryTransactionOperations")
|
||||||
private val transactionOperations: TransactionOperations,
|
private val transactionOperations: TransactionOperations,
|
||||||
|
private val clock: Clock = Clock.systemUTC(),
|
||||||
) {
|
) {
|
||||||
private val log = LoggerFactory.getLogger(this::class.java)
|
private val log = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
@@ -55,7 +58,7 @@ class RequestExpiryService(
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
val maxBatches = requestExpiryProperties.maxBatchesPerRun.coerceAtLeast(1)
|
val maxBatches = requestExpiryProperties.maxBatchesPerRun.coerceAtLeast(1)
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now(clock)
|
||||||
|
|
||||||
var expiredTotal = 0
|
var expiredTotal = 0
|
||||||
var batchIndex = 0
|
var batchIndex = 0
|
||||||
|
|||||||
+5
-2
@@ -38,6 +38,8 @@ import org.springframework.data.domain.PageRequest
|
|||||||
import org.springframework.data.domain.Sort
|
import org.springframework.data.domain.Sort
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@@ -53,6 +55,7 @@ class RequestService(
|
|||||||
private val requestGridProjectionService: RequestGridProjectionService,
|
private val requestGridProjectionService: RequestGridProjectionService,
|
||||||
private val requestLifecycleService: RequestLifecycleService,
|
private val requestLifecycleService: RequestLifecycleService,
|
||||||
private val entityManager: EntityManager,
|
private val entityManager: EntityManager,
|
||||||
|
private val clock: Clock = Clock.systemUTC(),
|
||||||
) {
|
) {
|
||||||
@Transactional
|
@Transactional
|
||||||
fun createRequest(request: CreateRequestDto): CreateRequestResponseDto {
|
fun createRequest(request: CreateRequestDto): CreateRequestResponseDto {
|
||||||
@@ -65,7 +68,7 @@ class RequestService(
|
|||||||
"beginDateTime должен быть раньше endDateTime"
|
"beginDateTime должен быть раньше endDateTime"
|
||||||
}
|
}
|
||||||
|
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now(clock)
|
||||||
val normalizedGeometry = geometryService.parsePolygonalGeometry(request.geometry)
|
val normalizedGeometry = geometryService.parsePolygonalGeometry(request.geometry)
|
||||||
val normalizedGeometryWkt = geometryService.toWkt(normalizedGeometry)
|
val normalizedGeometryWkt = geometryService.toWkt(normalizedGeometry)
|
||||||
val geometryArea = geometryService.area(normalizedGeometry)
|
val geometryArea = geometryService.area(normalizedGeometry)
|
||||||
@@ -159,7 +162,7 @@ class RequestService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun deleteRequest(id: UUID): DeleteRequestResponseDto? {
|
fun deleteRequest(id: UUID): DeleteRequestResponseDto? {
|
||||||
val request = requestRepository.findById(id).orElse(null) ?: return null
|
val request = requestRepository.findById(id).orElse(null) ?: return null
|
||||||
val deletedAt = request.deletedAt ?: LocalDateTime.now()
|
val deletedAt = request.deletedAt ?: UtcDateTimes.now(clock)
|
||||||
request.status = RequestStatus.DELETED
|
request.status = RequestStatus.DELETED
|
||||||
request.deletedAt = deletedAt
|
request.deletedAt = deletedAt
|
||||||
request.updatedAt = deletedAt
|
request.updatedAt = deletedAt
|
||||||
|
|||||||
+4
-1
@@ -14,6 +14,8 @@ import org.springframework.dao.DuplicateKeyException
|
|||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.support.TransactionCallback
|
import org.springframework.transaction.support.TransactionCallback
|
||||||
import org.springframework.transaction.support.TransactionOperations
|
import org.springframework.transaction.support.TransactionOperations
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.Clock
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
@@ -35,6 +37,7 @@ class RouteMatchingService(
|
|||||||
|
|
||||||
@param:Qualifier("routeMatchingTransactionOperations")
|
@param:Qualifier("routeMatchingTransactionOperations")
|
||||||
private val routeMatchingTransactionOperations: TransactionOperations,
|
private val routeMatchingTransactionOperations: TransactionOperations,
|
||||||
|
private val clock: Clock = Clock.systemUTC(),
|
||||||
) {
|
) {
|
||||||
private val log = LoggerFactory.getLogger(this::class.java)
|
private val log = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
@@ -43,7 +46,7 @@ class RouteMatchingService(
|
|||||||
|
|
||||||
val routeId = route.routeId
|
val routeId = route.routeId
|
||||||
val routeRollRange = route.rollAngle
|
val routeRollRange = route.rollAngle
|
||||||
val matchedAt: LocalDateTime = LocalDateTime.now()
|
val matchedAt: LocalDateTime = UtcDateTimes.now(clock)
|
||||||
val routeGeometry = geometryService.parsePolygonalGeometry(route.geometry)
|
val routeGeometry = geometryService.parsePolygonalGeometry(route.geometry)
|
||||||
|
|
||||||
val candidateRequestIds = requestRepository.findRequestIdsForRoute(
|
val candidateRequestIds = requestRepository.findRequestIdsForRoute(
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package space.nstart.pcp.routepassportconsumer.config
|
||||||
|
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.format.FormatterRegistry
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeConfiguration : WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun clock(): Clock = Clock.systemUTC()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun routeProcessingUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-route-processing-service-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFormatters(registry: FormatterRegistry) {
|
||||||
|
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
|
||||||
|
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -18,6 +18,7 @@ import com.fasterxml.jackson.databind.ObjectMapper as JpaJsonObjectMapper
|
|||||||
import tools.jackson.databind.ObjectMapper
|
import tools.jackson.databind.ObjectMapper
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сохраняет разобранный route passport и outbox-события в одной DB transaction.
|
* Сохраняет разобранный route passport и outbox-события в одной DB transaction.
|
||||||
@@ -38,7 +39,7 @@ class RoutePassportIngestionService(
|
|||||||
rawPayload: String,
|
rawPayload: String,
|
||||||
sourceRecord: RoutePassportSourceRecordMetadata,
|
sourceRecord: RoutePassportSourceRecordMetadata,
|
||||||
) {
|
) {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val routePassportDto = parsedMessage.routePassportDto
|
val routePassportDto = parsedMessage.routePassportDto
|
||||||
val existingRoutePassport = routePassportRepository.findById(routePassportDto.routeId)
|
val existingRoutePassport = routePassportRepository.findById(routePassportDto.routeId)
|
||||||
val routeCreated = existingRoutePassport.isEmpty
|
val routeCreated = existingRoutePassport.isEmpty
|
||||||
|
|||||||
+3
-3
@@ -8,11 +8,11 @@ import space.nstart.pcp.pcp_types_lib.dto.routes.RawPassportEnvelopeDto
|
|||||||
import space.nstart.pcp.pcp_types_lib.dto.routes.RoutePassportDto
|
import space.nstart.pcp.pcp_types_lib.dto.routes.RoutePassportDto
|
||||||
import space.nstart.pcp.pcp_types_lib.dto.routes.RoutePassportMessageMetadata
|
import space.nstart.pcp.pcp_types_lib.dto.routes.RoutePassportMessageMetadata
|
||||||
import space.nstart.pcp.routepassportconsumer.exception.RoutePassportContractException
|
import space.nstart.pcp.routepassportconsumer.exception.RoutePassportContractException
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import tools.jackson.core.JacksonException
|
import tools.jackson.core.JacksonException
|
||||||
import tools.jackson.databind.ObjectMapper
|
import tools.jackson.databind.ObjectMapper
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,8 +63,8 @@ class RoutePassportJsonParserService(
|
|||||||
routeNameShort = collection,
|
routeNameShort = collection,
|
||||||
orbitNumber = properties.orbitNumber.toLong(),
|
orbitNumber = properties.orbitNumber.toLong(),
|
||||||
orbitState = properties.orbitState,
|
orbitState = properties.orbitState,
|
||||||
intervalBegin = LocalDateTime.parse(properties.intervalBegin),
|
intervalBegin = UtcDateTimes.parse(properties.intervalBegin),
|
||||||
intervalEnd = LocalDateTime.parse(properties.intervalEnd),
|
intervalEnd = UtcDateTimes.parse(properties.intervalEnd),
|
||||||
rollAngle = properties.rollAngle.toAngleRangeDto(fieldName = "rollAngle"),
|
rollAngle = properties.rollAngle.toAngleRangeDto(fieldName = "rollAngle"),
|
||||||
visirAngle = properties.visirAngle.toAngleRangeDto(fieldName = "visirAngle"),
|
visirAngle = properties.visirAngle.toAngleRangeDto(fieldName = "visirAngle"),
|
||||||
resolutionRange = properties.resolutionRange.toBigDecimal(),
|
resolutionRange = properties.resolutionRange.toBigDecimal(),
|
||||||
|
|||||||
+4
-3
@@ -10,6 +10,7 @@ import space.nstart.pcp.routepassportconsumer.repository.RoutePassportOutboxRepo
|
|||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Owns transactional claim and status transitions for route passport outbox rows.
|
* Owns transactional claim and status transitions for route passport outbox rows.
|
||||||
@@ -32,7 +33,7 @@ class RoutePassportOutboxService(
|
|||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val lockedUntil = now.plus(Duration.ofMillis(outboxProperties.lockTimeoutMs))
|
val lockedUntil = now.plus(Duration.ofMillis(outboxProperties.lockTimeoutMs))
|
||||||
val claimedRows = routePassportOutboxRepository.findDueForUpdateSkipLocked(outboxProperties.batchSize)
|
val claimedRows = routePassportOutboxRepository.findDueForUpdateSkipLocked(outboxProperties.batchSize)
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ class RoutePassportOutboxService(
|
|||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
fun markPublished(outboxId: UUID) {
|
fun markPublished(outboxId: UUID) {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val outbox = findOutbox(outboxId)
|
val outbox = findOutbox(outboxId)
|
||||||
|
|
||||||
outbox.status = RoutePassportOutboxStatus.PUBLISHED
|
outbox.status = RoutePassportOutboxStatus.PUBLISHED
|
||||||
@@ -99,7 +100,7 @@ class RoutePassportOutboxService(
|
|||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
fun markRetryOrFailed(outboxId: UUID, exception: Exception) {
|
fun markRetryOrFailed(outboxId: UUID, exception: Exception) {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val outbox = findOutbox(outboxId)
|
val outbox = findOutbox(outboxId)
|
||||||
|
|
||||||
outbox.attempts += 1
|
outbox.attempts += 1
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package space.nstart.pcp.pcp_satellite_catalog_service.configuration
|
||||||
|
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.format.FormatterRegistry
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeConfiguration : WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun clock(): Clock = Clock.systemUTC()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun satelliteCatalogUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-satellite-catalog-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFormatters(registry: FormatterRegistry) {
|
||||||
|
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
|
||||||
|
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -1,15 +1,24 @@
|
|||||||
package space.nstart.pcp_tgu_service.config
|
package space.nstart.pcp_tgu_service.config
|
||||||
|
|
||||||
/** Настраивает Jackson-сериализацию для payload'ов клиента Camunda. */
|
/** Настраивает Jackson-сериализацию для payload'ов клиента Camunda. */
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature
|
import com.fasterxml.jackson.databind.SerializationFeature
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import io.camunda.client.api.JsonMapper as CamundaJsonMapper
|
import io.camunda.client.api.JsonMapper as CamundaJsonMapper
|
||||||
import io.camunda.client.impl.CamundaObjectMapper
|
import io.camunda.client.impl.CamundaObjectMapper
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
class CamundaJacksonConfig {
|
class CamundaJacksonConfig {
|
||||||
@@ -24,6 +33,7 @@ class CamundaJacksonConfig {
|
|||||||
return JsonMapper.builder()
|
return JsonMapper.builder()
|
||||||
.addModule(KotlinModule.Builder().build())
|
.addModule(KotlinModule.Builder().build())
|
||||||
.addModule(JavaTimeModule())
|
.addModule(JavaTimeModule())
|
||||||
|
.addModule(jackson2UtcLocalDateTimeModule())
|
||||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -34,3 +44,21 @@ class CamundaJacksonConfig {
|
|||||||
return CamundaObjectMapper(objectMapper)
|
return CamundaObjectMapper(objectMapper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun jackson2UtcLocalDateTimeModule(): SimpleModule = SimpleModule("pcp-tgu-service-jackson2-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeDeserializer())
|
||||||
|
|
||||||
|
private class Jackson2UtcLocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Jackson2UtcLocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package space.nstart.pcp_tgu_service.config
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||||
|
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.format.FormatterRegistry
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
import tools.jackson.core.JsonGenerator
|
||||||
|
import tools.jackson.core.JsonParser
|
||||||
|
import tools.jackson.databind.DeserializationContext
|
||||||
|
import tools.jackson.databind.SerializationContext
|
||||||
|
import tools.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import tools.jackson.databind.module.SimpleModule
|
||||||
|
import tools.jackson.databind.ser.std.StdSerializer
|
||||||
|
import java.time.Clock
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTC contract for REST JSON and query parameters.
|
||||||
|
*
|
||||||
|
* Domain/JPA models still keep LocalDateTime as UTC-naive values. External API accepts explicit
|
||||||
|
* UTC/offset strings and legacy naive strings, normalizes offsets to UTC, and writes responses
|
||||||
|
* as ISO-8601 UTC strings with Z.
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
class UtcTimeConfiguration : WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun clock(): Clock = Clock.systemUTC()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(WebClient.Builder::class)
|
||||||
|
fun webClientBuilder(): WebClient.Builder = WebClient.builder()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun tguUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
|
||||||
|
builder.addModule(
|
||||||
|
SimpleModule("pcp-tgu-service-utc-local-date-time")
|
||||||
|
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
|
||||||
|
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFormatters(registry: FormatterRegistry) {
|
||||||
|
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
|
||||||
|
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
|
||||||
|
gen.writeString(UtcDateTimes.format(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
|
||||||
|
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
|
||||||
|
return UtcDateTimes.parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-7
@@ -1,7 +1,6 @@
|
|||||||
package space.nstart.pcp_tgu_service.controller
|
package space.nstart.pcp_tgu_service.controller
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||||
import org.springframework.format.annotation.DateTimeFormat
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
@@ -90,12 +89,8 @@ class TestTguController(
|
|||||||
@GetMapping("/external-points/{noradId}")
|
@GetMapping("/external-points/{noradId}")
|
||||||
fun getExternalPoints(
|
fun getExternalPoints(
|
||||||
@PathVariable noradId: Long,
|
@PathVariable noradId: Long,
|
||||||
@RequestParam("time_start")
|
@RequestParam("time_start") timeStart: LocalDateTime,
|
||||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
@RequestParam("time_stop") timeStop: LocalDateTime
|
||||||
timeStart: LocalDateTime,
|
|
||||||
@RequestParam("time_stop")
|
|
||||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
|
||||||
timeStop: LocalDateTime
|
|
||||||
): String =
|
): String =
|
||||||
externalPointsClient.fetchRawVisibilityPayload(noradId, timeStart, timeStop)
|
externalPointsClient.fetchRawVisibilityPayload(noradId, timeStart, timeStop)
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -10,8 +10,8 @@ import java.util.UUID
|
|||||||
/**
|
/**
|
||||||
* DTO ответа REST API для плана.
|
* DTO ответа REST API для плана.
|
||||||
*
|
*
|
||||||
* Времена ([startTime]/[endTime]) — naive `LocalDateTime`, сериализуются как ISO-строка без зоны
|
* Времена ([startTime]/[endTime]) — UTC-naive `LocalDateTime` в домене/БД.
|
||||||
* (напр. `"2026-05-29T01:00:00"`). Хранение, домен и API работают на одних настенных часах.
|
* На REST-границе сериализуются как ISO-8601 UTC с `Z` (например `"2026-05-29T01:00:00Z"`).
|
||||||
*/
|
*/
|
||||||
data class PlanResponse(
|
data class PlanResponse(
|
||||||
|
|
||||||
@@ -21,10 +21,10 @@ data class PlanResponse(
|
|||||||
/** Идентификатор космического аппарата */
|
/** Идентификатор космического аппарата */
|
||||||
val spacecraftId: String,
|
val spacecraftId: String,
|
||||||
|
|
||||||
/** Время начала плана (ISO без зоны). */
|
/** Время начала плана (UTC). */
|
||||||
val startTime: LocalDateTime,
|
val startTime: LocalDateTime,
|
||||||
|
|
||||||
/** Время окончания плана (ISO без зоны). */
|
/** Время окончания плана (UTC). */
|
||||||
val endTime: LocalDateTime,
|
val endTime: LocalDateTime,
|
||||||
|
|
||||||
/** Идентификатор пункта закладки */
|
/** Идентификатор пункта закладки */
|
||||||
|
|||||||
+3
-2
@@ -8,6 +8,7 @@ import jakarta.persistence.Id
|
|||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persisted job на запуск короткого BPMN-процесса обновления слотов accepted plan.
|
* Persisted job на запуск короткого BPMN-процесса обновления слотов accepted plan.
|
||||||
@@ -39,10 +40,10 @@ class AcceptedPlanSlotsUpdateJobEntity(
|
|||||||
var lastError: String? = null,
|
var lastError: String? = null,
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
var createdAt: LocalDateTime = LocalDateTime.now(),
|
var createdAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
var updatedAt: LocalDateTime = LocalDateTime.now()
|
var updatedAt: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
||||||
constructor() : this(planId = UUID.randomUUID())
|
constructor() : this(planId = UUID.randomUUID())
|
||||||
|
|||||||
+3
-2
@@ -8,6 +8,7 @@ import jakarta.persistence.Id
|
|||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Одна попытка выдачи persisted plan через один BPMN process instance.
|
* Одна попытка выдачи persisted plan через один BPMN process instance.
|
||||||
@@ -45,10 +46,10 @@ class PlanIssueAttemptEntity(
|
|||||||
var finishedAt: LocalDateTime? = null,
|
var finishedAt: LocalDateTime? = null,
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
var createdAt: LocalDateTime = LocalDateTime.now(),
|
var createdAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
var updatedAt: LocalDateTime = LocalDateTime.now()
|
var updatedAt: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
||||||
constructor() : this(attemptId = UUID.randomUUID(), planId = UUID.randomUUID(), attemptNo = 0)
|
constructor() : this(attemptId = UUID.randomUUID(), planId = UUID.randomUUID(), attemptNo = 0)
|
||||||
|
|||||||
+3
-2
@@ -8,6 +8,7 @@ import jakarta.persistence.Id
|
|||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persisted business-plan с устойчивым planId по unique business key.
|
* Persisted business-plan с устойчивым planId по unique business key.
|
||||||
@@ -56,10 +57,10 @@ class PlannedPlanEntity(
|
|||||||
var dismissedAt: LocalDateTime? = null,
|
var dismissedAt: LocalDateTime? = null,
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
var createdAt: LocalDateTime = LocalDateTime.now(),
|
var createdAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
var updatedAt: LocalDateTime = LocalDateTime.now()
|
var updatedAt: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
||||||
constructor() : this(
|
constructor() : this(
|
||||||
|
|||||||
+2
-1
@@ -6,6 +6,7 @@ import jakarta.persistence.Id
|
|||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dedup-запись Kafka decision event по eventId.
|
* Dedup-запись Kafka decision event по eventId.
|
||||||
@@ -18,7 +19,7 @@ class ProcessedPlanDecisionEventEntity(
|
|||||||
var eventId: UUID,
|
var eventId: UUID,
|
||||||
|
|
||||||
@Column(name = "received_at", nullable = false)
|
@Column(name = "received_at", nullable = false)
|
||||||
var receivedAt: LocalDateTime = LocalDateTime.now()
|
var receivedAt: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
||||||
constructor() : this(eventId = UUID.randomUUID())
|
constructor() : this(eventId = UUID.randomUUID())
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ import jakarta.persistence.Id
|
|||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "spacecraft_planning_state")
|
@Table(name = "spacecraft_planning_state")
|
||||||
@@ -42,7 +43,7 @@ class SpacecraftPlanningStateEntity(
|
|||||||
var pausedAfterReject: Boolean = false,
|
var pausedAfterReject: Boolean = false,
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
var updatedAt: LocalDateTime = LocalDateTime.now()
|
var updatedAt: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
||||||
constructor() : this(spacecraftId = "")
|
constructor() : this(spacecraftId = "")
|
||||||
|
|||||||
+3
-2
@@ -5,6 +5,7 @@ import jakarta.persistence.Entity
|
|||||||
import jakarta.persistence.Id
|
import jakarta.persistence.Id
|
||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persisted snapshot КА из НСИ, используемый как вход для refresh jobs.
|
* Persisted snapshot КА из НСИ, используемый как вход для refresh jobs.
|
||||||
@@ -44,10 +45,10 @@ class SpacecraftSnapshotEntity(
|
|||||||
var active: Boolean = true,
|
var active: Boolean = true,
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
var createdAt: LocalDateTime = LocalDateTime.now(),
|
var createdAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
var updatedAt: LocalDateTime = LocalDateTime.now()
|
var updatedAt: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
||||||
constructor() : this(spacecraftId = "", noradId = 0)
|
constructor() : this(spacecraftId = "", noradId = 0)
|
||||||
|
|||||||
+3
-2
@@ -5,6 +5,7 @@ import jakarta.persistence.Entity
|
|||||||
import jakarta.persistence.Id
|
import jakarta.persistence.Id
|
||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Полный raw snapshot окон видимости КА из баллистики.
|
* Полный raw snapshot окон видимости КА из баллистики.
|
||||||
@@ -40,10 +41,10 @@ class SpacecraftVisibilitySnapshotEntity(
|
|||||||
var windowsCount: Int? = null,
|
var windowsCount: Int? = null,
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false)
|
@Column(name = "created_at", nullable = false)
|
||||||
var createdAt: LocalDateTime = LocalDateTime.now(),
|
var createdAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
var updatedAt: LocalDateTime = LocalDateTime.now()
|
var updatedAt: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
||||||
constructor() : this(spacecraftId = "", noradId = 0, payloadJson = "", payloadHash = "", fetchedAt = LocalDateTime.MIN)
|
constructor() : this(spacecraftId = "", noradId = 0, payloadJson = "", payloadHash = "", fetchedAt = LocalDateTime.MIN)
|
||||||
|
|||||||
+3
-2
@@ -7,6 +7,7 @@ import jakarta.persistence.Enumerated
|
|||||||
import jakarta.persistence.Id
|
import jakarta.persistence.Id
|
||||||
import jakarta.persistence.Table
|
import jakarta.persistence.Table
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Job на перечитывание окон видимости одного КА у баллистики.
|
* Job на перечитывание окон видимости одного КА у баллистики.
|
||||||
@@ -41,10 +42,10 @@ class VisibilityRefreshJobEntity(
|
|||||||
var rerunRequested: Boolean = false,
|
var rerunRequested: Boolean = false,
|
||||||
|
|
||||||
@Column(name = "requested_at", nullable = false)
|
@Column(name = "requested_at", nullable = false)
|
||||||
var requestedAt: LocalDateTime = LocalDateTime.now(),
|
var requestedAt: LocalDateTime = UtcDateTimes.now(),
|
||||||
|
|
||||||
@Column(name = "updated_at", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
var updatedAt: LocalDateTime = LocalDateTime.now()
|
var updatedAt: LocalDateTime = UtcDateTimes.now()
|
||||||
) {
|
) {
|
||||||
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
/** Создаёт пустой экземпляр сущности, необходимый для JPA. */
|
||||||
constructor() : this(spacecraftId = "")
|
constructor() : this(spacecraftId = "")
|
||||||
|
|||||||
+6
-4
@@ -13,6 +13,7 @@ import org.springframework.web.reactive.function.client.WebClientException
|
|||||||
import org.springframework.web.reactive.function.client.WebClientResponseException
|
import org.springframework.web.reactive.function.client.WebClientResponseException
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Клиент внешнего API с окнами видимости КА.
|
* Клиент внешнего API с окнами видимости КА.
|
||||||
@@ -20,6 +21,7 @@ import java.time.temporal.ChronoUnit
|
|||||||
@Component
|
@Component
|
||||||
class ExternalPointsClient(
|
class ExternalPointsClient(
|
||||||
externalApiProperties: ExternalApiProperties,
|
externalApiProperties: ExternalApiProperties,
|
||||||
|
webClientBuilder: WebClient.Builder,
|
||||||
private val platformService: PlatformService,
|
private val platformService: PlatformService,
|
||||||
private val planningProperties: PlanningProperties,
|
private val planningProperties: PlanningProperties,
|
||||||
private val visibilityPayloadParser: VisibilityPayloadParser
|
private val visibilityPayloadParser: VisibilityPayloadParser
|
||||||
@@ -27,7 +29,7 @@ class ExternalPointsClient(
|
|||||||
|
|
||||||
private val log = LoggerFactory.getLogger(javaClass)
|
private val log = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
private val webClient: WebClient = WebClient.builder()
|
private val webClient: WebClient = webClientBuilder
|
||||||
.baseUrl(externalApiProperties.baseUrl)
|
.baseUrl(externalApiProperties.baseUrl)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@@ -36,7 +38,7 @@ class ExternalPointsClient(
|
|||||||
* которые разрешены для работы TGU.
|
* которые разрешены для работы TGU.
|
||||||
*/
|
*/
|
||||||
fun fetchAllSpacecraftPoints(): List<SpacecraftPoints> {
|
fun fetchAllSpacecraftPoints(): List<SpacecraftPoints> {
|
||||||
val timeStart = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)
|
val timeStart = UtcDateTimes.now().truncatedTo(ChronoUnit.MINUTES)
|
||||||
val timeStop = timeStart.plusDays(planningProperties.externalPointsLookaheadDays)
|
val timeStop = timeStart.plusDays(planningProperties.externalPointsLookaheadDays)
|
||||||
|
|
||||||
return platformService.loadAllowedPlatforms()
|
return platformService.loadAllowedPlatforms()
|
||||||
@@ -93,8 +95,8 @@ class ExternalPointsClient(
|
|||||||
.uri { uriBuilder ->
|
.uri { uriBuilder ->
|
||||||
uriBuilder
|
uriBuilder
|
||||||
.path("/api/satellites/{satelliteId}/rva")
|
.path("/api/satellites/{satelliteId}/rva")
|
||||||
.queryParam("time_start", timeStart)
|
.queryParam("time_start", UtcDateTimes.format(timeStart))
|
||||||
.queryParam("time_stop", timeStop)
|
.queryParam("time_stop", UtcDateTimes.format(timeStop))
|
||||||
.build(satelliteId)
|
.build(satelliteId)
|
||||||
}
|
}
|
||||||
.retrieve()
|
.retrieve()
|
||||||
|
|||||||
+33
-5
@@ -4,6 +4,7 @@ import org.springframework.http.HttpStatus
|
|||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import org.springframework.web.reactive.function.client.WebClient
|
import org.springframework.web.reactive.function.client.WebClient
|
||||||
import org.springframework.web.server.ResponseStatusException
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import space.nstart.pcp_tgu_service.config.MissionPlaningProperties
|
import space.nstart.pcp_tgu_service.config.MissionPlaningProperties
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
@@ -23,10 +24,25 @@ data class MissionBlankRequest(
|
|||||||
val missionStop: LocalDateTime
|
val missionStop: LocalDateTime
|
||||||
)
|
)
|
||||||
|
|
||||||
@Component
|
private data class MissionCloneOutboundRequest(
|
||||||
class MissionPlaningClient(properties: MissionPlaningProperties) {
|
val mode: String,
|
||||||
|
val windowStart: String? = null,
|
||||||
|
val windowStop: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
private val webClient: WebClient = WebClient.builder()
|
private data class MissionBlankOutboundRequest(
|
||||||
|
val satelliteId: Long,
|
||||||
|
val missionStart: String,
|
||||||
|
val missionStop: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MissionPlaningClient(
|
||||||
|
properties: MissionPlaningProperties,
|
||||||
|
webClientBuilder: WebClient.Builder
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val webClient: WebClient = webClientBuilder
|
||||||
.baseUrl(properties.baseUrl)
|
.baseUrl(properties.baseUrl)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@@ -35,7 +51,7 @@ class MissionPlaningClient(properties: MissionPlaningProperties) {
|
|||||||
fun cloneMission(missionId: UUID, req: MissionCloneRequest): MissionCloneResponse =
|
fun cloneMission(missionId: UUID, req: MissionCloneRequest): MissionCloneResponse =
|
||||||
webClient.post()
|
webClient.post()
|
||||||
.uri("/api/missions/{id}/clone", missionId)
|
.uri("/api/missions/{id}/clone", missionId)
|
||||||
.bodyValue(req)
|
.bodyValue(req.toOutbound())
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.onStatus({ it.isError }) { resp ->
|
.onStatus({ it.isError }) { resp ->
|
||||||
resp.bodyToMono(String::class.java).defaultIfEmpty("").map { body ->
|
resp.bodyToMono(String::class.java).defaultIfEmpty("").map { body ->
|
||||||
@@ -53,11 +69,23 @@ class MissionPlaningClient(properties: MissionPlaningProperties) {
|
|||||||
"Пустой ответ mission-planing на клон миссии $missionId"
|
"Пустой ответ mission-planing на клон миссии $missionId"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun MissionCloneRequest.toOutbound(): MissionCloneOutboundRequest = MissionCloneOutboundRequest(
|
||||||
|
mode = mode,
|
||||||
|
windowStart = windowStart?.let(UtcDateTimes::format),
|
||||||
|
windowStop = windowStop?.let(UtcDateTimes::format)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun MissionBlankRequest.toOutbound(): MissionBlankOutboundRequest = MissionBlankOutboundRequest(
|
||||||
|
satelliteId = satelliteId,
|
||||||
|
missionStart = UtcDateTimes.format(missionStart),
|
||||||
|
missionStop = UtcDateTimes.format(missionStop)
|
||||||
|
)
|
||||||
|
|
||||||
/** Создаёт пустую миссию на окно (план новой цепочки «без истории»). */
|
/** Создаёт пустую миссию на окно (план новой цепочки «без истории»). */
|
||||||
fun createBlankMission(req: MissionBlankRequest): MissionCloneResponse =
|
fun createBlankMission(req: MissionBlankRequest): MissionCloneResponse =
|
||||||
webClient.post()
|
webClient.post()
|
||||||
.uri("/api/missions/blank")
|
.uri("/api/missions/blank")
|
||||||
.bodyValue(req)
|
.bodyValue(req.toOutbound())
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.onStatus({ it.isError }) { resp ->
|
.onStatus({ it.isError }) { resp ->
|
||||||
resp.bodyToMono(String::class.java).defaultIfEmpty("").map { body ->
|
resp.bodyToMono(String::class.java).defaultIfEmpty("").map { body ->
|
||||||
|
|||||||
+5
-17
@@ -6,10 +6,8 @@ import space.nstart.pcp_tgu_service.domain.InsertionPoint
|
|||||||
import space.nstart.pcp_tgu_service.domain.ObservationWindow
|
import space.nstart.pcp_tgu_service.domain.ObservationWindow
|
||||||
import space.nstart.pcp_tgu_service.domain.SpacecraftPoints
|
import space.nstart.pcp_tgu_service.domain.SpacecraftPoints
|
||||||
import space.nstart.pcp_tgu_service.dto.ExternalRvaItemDto
|
import space.nstart.pcp_tgu_service.dto.ExternalRvaItemDto
|
||||||
import java.time.Instant
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneId
|
|
||||||
import java.time.format.DateTimeParseException
|
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,21 +41,11 @@ class VisibilityPayloadParser(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Разбирает ISO-строку времени баллистики в naive `LocalDateTime`.
|
* Разбирает ISO-строку времени баллистики в UTC-naive `LocalDateTime`.
|
||||||
*
|
*
|
||||||
* Если время приходит с зоной (`...Z`/офсет), момент проецируем в локальный пояс JVM
|
* Явные UTC/offset-строки нормализуются в UTC, legacy naive-строки считаются UTC-naive.
|
||||||
* (`ZoneId.systemDefault()`): naive-время в системе — настенные часы локальной зоны, в которой
|
* Обрезаем до микросекунд — это эффективная точность persisted-планов в БД.
|
||||||
* работают все сервисы, без UTC-семантики. Naive-строку без зоны берём как есть.
|
|
||||||
*
|
|
||||||
* Обрезаем до микросекунд — это эффективная точность системы: `planned_plan.start_time/end_time`
|
|
||||||
* хранятся как Postgres `timestamp(6)`, при записи наносекундный хвост баллистики всё равно теряется.
|
|
||||||
* Без обрезки проекция (наносекунды) и persisted-планы (микросекунды) перестают совпадать по
|
|
||||||
* бизнес-ключу — ломается дедуп в read-пути и матчинг базы в [calculatePlansAfter].
|
|
||||||
*/
|
*/
|
||||||
private fun toLocalDateTime(value: String): LocalDateTime =
|
private fun toLocalDateTime(value: String): LocalDateTime =
|
||||||
try {
|
UtcDateTimes.parse(value).truncatedTo(ChronoUnit.MICROS)
|
||||||
LocalDateTime.ofInstant(Instant.parse(value), ZoneId.systemDefault())
|
|
||||||
} catch (_: DateTimeParseException) {
|
|
||||||
LocalDateTime.parse(value)
|
|
||||||
}.truncatedTo(ChronoUnit.MICROS)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -5,6 +5,7 @@ import org.springframework.transaction.annotation.Transactional
|
|||||||
import space.nstart.pcp_tgu_service.repository.AcceptedPlanSlotsUpdateJobRepository
|
import space.nstart.pcp_tgu_service.repository.AcceptedPlanSlotsUpdateJobRepository
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Создаёт persisted job обновления слотов для accepted plan.
|
* Создаёт persisted job обновления слотов для accepted plan.
|
||||||
@@ -16,6 +17,6 @@ class AcceptedPlanSlotsUpdateJobService(
|
|||||||
/** Idempotent enqueue: один planId получает максимум одну job. */
|
/** Idempotent enqueue: один planId получает максимум одну job. */
|
||||||
@Transactional
|
@Transactional
|
||||||
fun enqueue(planId: UUID) {
|
fun enqueue(planId: UUID) {
|
||||||
acceptedPlanSlotsUpdateJobRepository.insertPendingIfAbsent(planId, LocalDateTime.now())
|
acceptedPlanSlotsUpdateJobRepository.insertPendingIfAbsent(planId, UtcDateTimes.now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -11,6 +11,7 @@ import space.nstart.pcp_tgu_service.repository.AcceptedPlanSlotsUpdateJobReposit
|
|||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scale-out safe worker, который запускает updateAcceptedPlanSlots для accepted plan.
|
* Scale-out safe worker, который запускает updateAcceptedPlanSlots для accepted plan.
|
||||||
@@ -35,7 +36,7 @@ class AcceptedPlanSlotsUpdateWorker(
|
|||||||
/** Claim видим для тестов; в production защищён FOR UPDATE SKIP LOCKED. */
|
/** Claim видим для тестов; в production защищён FOR UPDATE SKIP LOCKED. */
|
||||||
fun claimJobs(limit: Int): List<AcceptedPlanSlotsUpdateJobEntity> =
|
fun claimJobs(limit: Int): List<AcceptedPlanSlotsUpdateJobEntity> =
|
||||||
transactionTemplate.execute {
|
transactionTemplate.execute {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val jobs = acceptedPlanSlotsUpdateJobRepository.findClaimableForUpdate(now, limit)
|
val jobs = acceptedPlanSlotsUpdateJobRepository.findClaimableForUpdate(now, limit)
|
||||||
jobs.forEach { job ->
|
jobs.forEach { job ->
|
||||||
job.status = AcceptedPlanSlotsUpdateJobStatus.RUNNING
|
job.status = AcceptedPlanSlotsUpdateJobStatus.RUNNING
|
||||||
@@ -69,7 +70,7 @@ class AcceptedPlanSlotsUpdateWorker(
|
|||||||
|
|
||||||
private fun markDone(planId: UUID, processInstanceKey: Long) {
|
private fun markDone(planId: UUID, processInstanceKey: Long) {
|
||||||
transactionTemplate.executeWithoutResult {
|
transactionTemplate.executeWithoutResult {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val job = acceptedPlanSlotsUpdateJobRepository.findById(planId).orElse(null) ?: return@executeWithoutResult
|
val job = acceptedPlanSlotsUpdateJobRepository.findById(planId).orElse(null) ?: return@executeWithoutResult
|
||||||
job.status = AcceptedPlanSlotsUpdateJobStatus.DONE
|
job.status = AcceptedPlanSlotsUpdateJobStatus.DONE
|
||||||
job.processInstanceKey = processInstanceKey
|
job.processInstanceKey = processInstanceKey
|
||||||
@@ -83,7 +84,7 @@ class AcceptedPlanSlotsUpdateWorker(
|
|||||||
|
|
||||||
private fun markAmbiguous(planId: UUID, processInstanceKey: Long?, ex: Exception) {
|
private fun markAmbiguous(planId: UUID, processInstanceKey: Long?, ex: Exception) {
|
||||||
transactionTemplate.executeWithoutResult {
|
transactionTemplate.executeWithoutResult {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val job = acceptedPlanSlotsUpdateJobRepository.findById(planId).orElse(null) ?: return@executeWithoutResult
|
val job = acceptedPlanSlotsUpdateJobRepository.findById(planId).orElse(null) ?: return@executeWithoutResult
|
||||||
job.status = AcceptedPlanSlotsUpdateJobStatus.AMBIGUOUS
|
job.status = AcceptedPlanSlotsUpdateJobStatus.AMBIGUOUS
|
||||||
job.processInstanceKey = processInstanceKey ?: job.processInstanceKey
|
job.processInstanceKey = processInstanceKey ?: job.processInstanceKey
|
||||||
|
|||||||
+2
-1
@@ -18,6 +18,7 @@ import space.nstart.pcp_tgu_service.repository.PlannedPlanRepository
|
|||||||
import space.nstart.pcp_tgu_service.repository.SpacecraftPlanningStateJpaRepository
|
import space.nstart.pcp_tgu_service.repository.SpacecraftPlanningStateJpaRepository
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Создаёт план новой цепочки (вкладка «Планы», действие оператора «Создать план») и сразу считает
|
* Создаёт план новой цепочки (вкладка «Планы», действие оператора «Создать план») и сразу считает
|
||||||
@@ -46,7 +47,7 @@ class CreateChainPlanUseCase(
|
|||||||
private val log = LoggerFactory.getLogger(javaClass)
|
private val log = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
fun createChainPlan(req: CreateChainPlanRequest): CreateChainPlanResponse {
|
fun createChainPlan(req: CreateChainPlanRequest): CreateChainPlanResponse {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
|
|
||||||
if (!req.windowStop.isAfter(req.windowStart)) {
|
if (!req.windowStop.isAfter(req.windowStart)) {
|
||||||
throw ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, "windowStop должен быть позже windowStart")
|
throw ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, "windowStop должен быть позже windowStart")
|
||||||
|
|||||||
+2
-1
@@ -16,6 +16,7 @@ import space.nstart.pcp_tgu_service.repository.PlannedPlanRepository
|
|||||||
import space.nstart.pcp_tgu_service.repository.SpacecraftPlanningStateJpaRepository
|
import space.nstart.pcp_tgu_service.repository.SpacecraftPlanningStateJpaRepository
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Создаёт черновик плана (DRAFT) на основе существующего базового плана.
|
* Создаёт черновик плана (DRAFT) на основе существующего базового плана.
|
||||||
@@ -45,7 +46,7 @@ class CreateDraftUseCase(
|
|||||||
val base = plannedPlanRepository.findById(basePlanId)
|
val base = plannedPlanRepository.findById(basePlanId)
|
||||||
.orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND, "Plan $basePlanId not found") }
|
.orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND, "Plan $basePlanId not found") }
|
||||||
|
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
|
|
||||||
return when (req.mode) {
|
return when (req.mode) {
|
||||||
DraftMode.REPLACE -> createReplaceDraft(base, now)
|
DraftMode.REPLACE -> createReplaceDraft(base, now)
|
||||||
|
|||||||
+4
-3
@@ -9,6 +9,7 @@ import space.nstart.pcp_tgu_service.entity.PlannedPlanStatus
|
|||||||
import space.nstart.pcp_tgu_service.repository.PlannedPlanRepository
|
import space.nstart.pcp_tgu_service.repository.PlannedPlanRepository
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* «Снять с учёта» / «Вернуть на учёт» терминально-негативный план.
|
* «Снять с учёта» / «Вернуть на учёт» терминально-негативный план.
|
||||||
@@ -44,8 +45,8 @@ class DismissPlanUseCase(
|
|||||||
log.info("План {} уже снят с учёта ({}) — идемпотентно", planId, plan.dismissedAt)
|
log.info("План {} уже снят с учёта ({}) — идемпотентно", planId, plan.dismissedAt)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
plan.dismissedAt = LocalDateTime.now()
|
plan.dismissedAt = UtcDateTimes.now()
|
||||||
plan.updatedAt = LocalDateTime.now()
|
plan.updatedAt = UtcDateTimes.now()
|
||||||
plannedPlanRepository.save(plan)
|
plannedPlanRepository.save(plan)
|
||||||
log.info("План {} (status={}) снят с учёта", planId, plan.status)
|
log.info("План {} (status={}) снят с учёта", planId, plan.status)
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,7 @@ class DismissPlanUseCase(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
plan.dismissedAt = null
|
plan.dismissedAt = null
|
||||||
plan.updatedAt = LocalDateTime.now()
|
plan.updatedAt = UtcDateTimes.now()
|
||||||
plannedPlanRepository.save(plan)
|
plannedPlanRepository.save(plan)
|
||||||
log.info("План {} (status={}) возвращён на учёт", planId, plan.status)
|
log.info("План {} (status={}) возвращён на учёт", planId, plan.status)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -20,6 +20,7 @@ import space.nstart.pcp_tgu_service.repository.ProcessedPlanDecisionEventReposit
|
|||||||
import space.nstart.pcp_tgu_service.repository.SpacecraftPlanningStateJpaRepository
|
import space.nstart.pcp_tgu_service.repository.SpacecraftPlanningStateJpaRepository
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Обрабатывает decision и закладочные feedback-события идемпотентно по eventId.
|
* Обрабатывает decision и закладочные feedback-события идемпотентно по eventId.
|
||||||
@@ -132,7 +133,7 @@ class HandlePlanDecisionUseCase(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
plan.status = PlannedPlanStatus.LAID_IN
|
plan.status = PlannedPlanStatus.LAID_IN
|
||||||
plan.updatedAt = now
|
plan.updatedAt = now
|
||||||
|
|
||||||
@@ -213,7 +214,7 @@ class HandlePlanDecisionUseCase(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
plan.status = terminalStatus
|
plan.status = terminalStatus
|
||||||
plan.updatedAt = now
|
plan.updatedAt = now
|
||||||
plannedPlanRepository.save(plan)
|
plannedPlanRepository.save(plan)
|
||||||
@@ -225,7 +226,7 @@ class HandlePlanDecisionUseCase(
|
|||||||
message: PlanDecisionMessage,
|
message: PlanDecisionMessage,
|
||||||
attempt: PlanIssueAttemptEntity
|
attempt: PlanIssueAttemptEntity
|
||||||
) {
|
) {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
attempt.status = when (message.decision) {
|
attempt.status = when (message.decision) {
|
||||||
PlanDecision.ACCEPTED -> PlanIssueAttemptStatus.ACCEPTED
|
PlanDecision.ACCEPTED -> PlanIssueAttemptStatus.ACCEPTED
|
||||||
PlanDecision.REJECTED -> PlanIssueAttemptStatus.REJECTED
|
PlanDecision.REJECTED -> PlanIssueAttemptStatus.REJECTED
|
||||||
@@ -241,7 +242,7 @@ class HandlePlanDecisionUseCase(
|
|||||||
attempt: PlanIssueAttemptEntity,
|
attempt: PlanIssueAttemptEntity,
|
||||||
plan: PlannedPlanEntity
|
plan: PlannedPlanEntity
|
||||||
) {
|
) {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
recordDecisionOnAttempt(message, attempt)
|
recordDecisionOnAttempt(message, attempt)
|
||||||
|
|
||||||
plan.status = PlannedPlanStatus.AWAITING_LAYIN
|
plan.status = PlannedPlanStatus.AWAITING_LAYIN
|
||||||
@@ -262,7 +263,7 @@ class HandlePlanDecisionUseCase(
|
|||||||
plan: PlannedPlanEntity,
|
plan: PlannedPlanEntity,
|
||||||
state: SpacecraftPlanningStateEntity
|
state: SpacecraftPlanningStateEntity
|
||||||
) {
|
) {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
recordDecisionOnAttempt(message, attempt)
|
recordDecisionOnAttempt(message, attempt)
|
||||||
|
|
||||||
plan.status = PlannedPlanStatus.REJECTED
|
plan.status = PlannedPlanStatus.REJECTED
|
||||||
@@ -307,7 +308,7 @@ class HandlePlanDecisionUseCase(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun claimEvent(eventId: java.util.UUID): Boolean =
|
private fun claimEvent(eventId: java.util.UUID): Boolean =
|
||||||
processedPlanDecisionEventRepository.insertIfAbsent(eventId, LocalDateTime.now()) != 0
|
processedPlanDecisionEventRepository.insertIfAbsent(eventId, UtcDateTimes.now()) != 0
|
||||||
|
|
||||||
private fun getOrCreateLockedState(spacecraftId: String): SpacecraftPlanningStateEntity {
|
private fun getOrCreateLockedState(spacecraftId: String): SpacecraftPlanningStateEntity {
|
||||||
spacecraftPlanningStateJpaRepository.findById(spacecraftId).orElse(null)
|
spacecraftPlanningStateJpaRepository.findById(spacecraftId).orElse(null)
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ import space.nstart.pcp_tgu_service.dto.StationParamsChangedMessage
|
|||||||
import space.nstart.pcp_tgu_service.repository.ProcessedPlanDecisionEventRepository
|
import space.nstart.pcp_tgu_service.repository.ProcessedPlanDecisionEventRepository
|
||||||
import space.nstart.pcp_tgu_service.repository.SpacecraftSnapshotRepository
|
import space.nstart.pcp_tgu_service.repository.SpacecraftSnapshotRepository
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* На изменение параметров станций перезапрашивает ЗРВ у баллистики по всем активным КА.
|
* На изменение параметров станций перезапрашивает ЗРВ у баллистики по всем активным КА.
|
||||||
@@ -25,7 +26,7 @@ class HandleStationParamsChangedUseCase(
|
|||||||
/** Идемпотентно по eventId; ставит refresh job по всем активным КА. */
|
/** Идемпотентно по eventId; ставит refresh job по всем активным КА. */
|
||||||
@Transactional
|
@Transactional
|
||||||
fun handle(message: StationParamsChangedMessage) {
|
fun handle(message: StationParamsChangedMessage) {
|
||||||
val claimed = processedPlanDecisionEventRepository.insertIfAbsent(message.eventId, LocalDateTime.now()) != 0
|
val claimed = processedPlanDecisionEventRepository.insertIfAbsent(message.eventId, UtcDateTimes.now()) != 0
|
||||||
if (!claimed) {
|
if (!claimed) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -20,6 +20,7 @@ import space.nstart.pcp_tgu_service.repository.SpacecraftPlanningStateJpaReposit
|
|||||||
import space.nstart.pcp_tgu_service.repository.SpacecraftSnapshotRepository
|
import space.nstart.pcp_tgu_service.repository.SpacecraftSnapshotRepository
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Выдаёт дозревшие планы. В on-demand-модели голова цепочки считается из снапшота ЗРВ
|
* Выдаёт дозревшие планы. В on-demand-модели голова цепочки считается из снапшота ЗРВ
|
||||||
@@ -83,7 +84,7 @@ class IssueDuePlanWorker(
|
|||||||
|
|
||||||
/** Готовит выдачу головы проекции для КА под lock'ом состояния (scale-out safe). */
|
/** Готовит выдачу головы проекции для КА под lock'ом состояния (scale-out safe). */
|
||||||
private fun prepareDueForSpacecraft(spacecraftId: String): PreparedAttempt? {
|
private fun prepareDueForSpacecraft(spacecraftId: String): PreparedAttempt? {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val state = getOrCreateLockedState(spacecraftId)
|
val state = getOrCreateLockedState(spacecraftId)
|
||||||
// Форвард-цепочку не выдаём, пока по КА есть выдача «в работе»: ISSUING или WAITING_DECISION
|
// Форвард-цепочку не выдаём, пока по КА есть выдача «в работе»: ISSUING или WAITING_DECISION
|
||||||
// в пределах grace после старта. Прошедший grace больше не блокирует — молчаливо продолжаем
|
// в пределах grace после старта. Прошедший grace больше не блокирует — молчаливо продолжаем
|
||||||
@@ -154,7 +155,7 @@ class IssueDuePlanWorker(
|
|||||||
.maxByOrNull { plan -> plan.endTime }?.planId ?: fallback
|
.maxByOrNull { plan -> plan.endTime }?.planId ?: fallback
|
||||||
|
|
||||||
private fun prepareSpecificPlan(planId: UUID): PreparedAttempt? {
|
private fun prepareSpecificPlan(planId: UUID): PreparedAttempt? {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val plan = plannedPlanRepository.findById(planId).orElse(null) ?: return null
|
val plan = plannedPlanRepository.findById(planId).orElse(null) ?: return null
|
||||||
val state = getOrCreateLockedState(plan.spacecraftId)
|
val state = getOrCreateLockedState(plan.spacecraftId)
|
||||||
assertReissuableInSlot(plan, now)
|
assertReissuableInSlot(plan, now)
|
||||||
@@ -176,7 +177,7 @@ class IssueDuePlanWorker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareDraftPromotion(draftPlanId: UUID): PreparedAttempt? {
|
private fun prepareDraftPromotion(draftPlanId: UUID): PreparedAttempt? {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val plan = plannedPlanRepository.findById(draftPlanId).orElse(null) ?: return null
|
val plan = plannedPlanRepository.findById(draftPlanId).orElse(null) ?: return null
|
||||||
if (plan.status != PlannedPlanStatus.DRAFT) {
|
if (plan.status != PlannedPlanStatus.DRAFT) {
|
||||||
throw ResponseStatusException(
|
throw ResponseStatusException(
|
||||||
@@ -326,7 +327,7 @@ class IssueDuePlanWorker(
|
|||||||
|
|
||||||
private fun markStarted(response: ProcessStartResponse) {
|
private fun markStarted(response: ProcessStartResponse) {
|
||||||
transactionTemplate.executeWithoutResult {
|
transactionTemplate.executeWithoutResult {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val attempt = planIssueAttemptRepository.findById(response.attemptId).orElse(null) ?: return@executeWithoutResult
|
val attempt = planIssueAttemptRepository.findById(response.attemptId).orElse(null) ?: return@executeWithoutResult
|
||||||
val plan = plannedPlanRepository.findById(response.planId).orElse(null) ?: return@executeWithoutResult
|
val plan = plannedPlanRepository.findById(response.planId).orElse(null) ?: return@executeWithoutResult
|
||||||
|
|
||||||
@@ -350,7 +351,7 @@ class IssueDuePlanWorker(
|
|||||||
|
|
||||||
private fun markAmbiguous(preparedAttempt: PreparedAttempt) {
|
private fun markAmbiguous(preparedAttempt: PreparedAttempt) {
|
||||||
transactionTemplate.executeWithoutResult {
|
transactionTemplate.executeWithoutResult {
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
val attempt = planIssueAttemptRepository.findById(preparedAttempt.attemptId).orElse(null) ?: return@executeWithoutResult
|
val attempt = planIssueAttemptRepository.findById(preparedAttempt.attemptId).orElse(null) ?: return@executeWithoutResult
|
||||||
val plan = plannedPlanRepository.findById(preparedAttempt.planId).orElse(null) ?: return@executeWithoutResult
|
val plan = plannedPlanRepository.findById(preparedAttempt.planId).orElse(null) ?: return@executeWithoutResult
|
||||||
attempt.status = PlanIssueAttemptStatus.AMBIGUOUS
|
attempt.status = PlanIssueAttemptStatus.AMBIGUOUS
|
||||||
|
|||||||
+2
-1
@@ -12,6 +12,7 @@ import space.nstart.pcp_tgu_service.repository.PlannedPlanRepository
|
|||||||
import space.nstart.pcp_tgu_service.repository.SpacecraftSnapshotRepository
|
import space.nstart.pcp_tgu_service.repository.SpacecraftSnapshotRepository
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read-сервис планов: объединяет persisted (выданные, история — вечно) с вычисленной
|
* Read-сервис планов: объединяет persisted (выданные, история — вечно) с вычисленной
|
||||||
@@ -80,7 +81,7 @@ class PlanQueryService(
|
|||||||
.sortedBy { response -> response.startTime }
|
.sortedBy { response -> response.startTime }
|
||||||
|
|
||||||
private fun historySince(historyDays: Long): LocalDateTime =
|
private fun historySince(historyDays: Long): LocalDateTime =
|
||||||
LocalDateTime.now().minusDays(historyDays.coerceAtLeast(0))
|
UtcDateTimes.now().minusDays(historyDays.coerceAtLeast(0))
|
||||||
|
|
||||||
private fun PlannedPlanEntity.businessKey(): String =
|
private fun PlannedPlanEntity.businessKey(): String =
|
||||||
"$spacecraftId|$startTime|$endTime|$kppId"
|
"$spacecraftId|$startTime|$endTime|$kppId"
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ import space.nstart.pcp_tgu_service.integration.api.VisibilityPayloadParser
|
|||||||
import space.nstart.pcp_tgu_service.repository.SpacecraftVisibilitySnapshotRepository
|
import space.nstart.pcp_tgu_service.repository.SpacecraftVisibilitySnapshotRepository
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сохраняет raw visibility payload. Расписание считается on-demand из снапшота
|
* Сохраняет raw visibility payload. Расписание считается on-demand из снапшота
|
||||||
@@ -25,7 +26,7 @@ class SpacecraftVisibilitySnapshotService(
|
|||||||
val payloadHash = sha256(payloadJson)
|
val payloadHash = sha256(payloadJson)
|
||||||
val existing = spacecraftVisibilitySnapshotRepository.findById(spacecraftId).orElse(null)
|
val existing = spacecraftVisibilitySnapshotRepository.findById(spacecraftId).orElse(null)
|
||||||
val metadata = runCatching { extractMetadata(spacecraftId, payloadJson) }.getOrNull()
|
val metadata = runCatching { extractMetadata(spacecraftId, payloadJson) }.getOrNull()
|
||||||
val now = LocalDateTime.now()
|
val now = UtcDateTimes.now()
|
||||||
|
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
spacecraftVisibilitySnapshotRepository.save(
|
spacecraftVisibilitySnapshotRepository.save(
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user