fix(polygon): separate dataset sources from runs
This commit is contained in:
@@ -73,6 +73,9 @@ export interface GroundReviewFrameMetrics {
|
||||
export interface GroundReviewFrameSummary {
|
||||
sequence: number;
|
||||
frameId: string;
|
||||
datasetSequenceId: string;
|
||||
datasetFrameNumber: number;
|
||||
sensorTimestampNs: string;
|
||||
sourcePointCount: number;
|
||||
pointCount: number;
|
||||
current: GroundReviewFrameMetrics;
|
||||
@@ -108,6 +111,8 @@ type QualificationFetch = (
|
||||
|
||||
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
||||
const SHA256 = /^[a-f0-9]{64}$/;
|
||||
const GOOSE_FRAME_ID =
|
||||
/^([A-Za-z0-9][A-Za-z0-9_-]{0,95})__([0-9]{4})_([0-9]{16,20})$/;
|
||||
|
||||
function record(value: unknown, field: string): Record<string, unknown> {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
@@ -373,7 +378,7 @@ function reviewFrameMetrics(value: unknown, field: string): GroundReviewFrameMet
|
||||
export function decodeGroundReview(payload: unknown): GroundReview {
|
||||
const source = record(payload, "ground review");
|
||||
if (
|
||||
source.schema_version !== "missioncore.polygon-ground-review/v1"
|
||||
source.schema_version !== "missioncore.polygon-ground-review/v2"
|
||||
|| source.access !== "read-only"
|
||||
) {
|
||||
throw new GroundQualificationContractError("Неизвестная схема покадрового просмотра.");
|
||||
@@ -386,9 +391,36 @@ export function decodeGroundReview(payload: unknown): GroundReview {
|
||||
if (sequence !== index) {
|
||||
throw new GroundQualificationContractError("Кадры просмотра идут не по порядку.");
|
||||
}
|
||||
const frameId = id(frame.frame_id, `frames[${index}].frame_id`);
|
||||
const frameIdentity = parseGooseFrameId(frameId);
|
||||
const datasetSequenceId = id(
|
||||
frame.dataset_sequence_id,
|
||||
`frames[${index}].dataset_sequence_id`,
|
||||
);
|
||||
const datasetFrameNumber = integer(
|
||||
frame.dataset_frame_number,
|
||||
`frames[${index}].dataset_frame_number`,
|
||||
);
|
||||
const sensorTimestampNs = text(
|
||||
frame.sensor_timestamp_ns,
|
||||
`frames[${index}].sensor_timestamp_ns`,
|
||||
20,
|
||||
);
|
||||
if (
|
||||
datasetSequenceId !== frameIdentity.datasetSequenceId
|
||||
|| datasetFrameNumber !== frameIdentity.datasetFrameNumber
|
||||
|| sensorTimestampNs !== frameIdentity.sensorTimestampNs
|
||||
) {
|
||||
throw new GroundQualificationContractError(
|
||||
`frames[${index}] противоречит идентичности GOOSE-кадра.`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
sequence,
|
||||
frameId: id(frame.frame_id, `frames[${index}].frame_id`),
|
||||
frameId,
|
||||
datasetSequenceId,
|
||||
datasetFrameNumber,
|
||||
sensorTimestampNs,
|
||||
sourcePointCount: integer(
|
||||
frame.source_point_count,
|
||||
`frames[${index}].source_point_count`,
|
||||
@@ -432,6 +464,26 @@ export function decodeGroundReview(payload: unknown): GroundReview {
|
||||
};
|
||||
}
|
||||
|
||||
export interface GooseFrameIdentity {
|
||||
datasetSequenceId: string;
|
||||
datasetFrameNumber: number;
|
||||
sensorTimestampNs: string;
|
||||
}
|
||||
|
||||
export function parseGooseFrameId(frameId: string): GooseFrameIdentity {
|
||||
const match = GOOSE_FRAME_ID.exec(frameId);
|
||||
if (!match) {
|
||||
throw new GroundQualificationContractError(
|
||||
"frame_id не содержит GOOSE sequence/frame/timestamp.",
|
||||
);
|
||||
}
|
||||
return {
|
||||
datasetSequenceId: match[1],
|
||||
datasetFrameNumber: Number(match[2]),
|
||||
sensorTimestampNs: match[3],
|
||||
};
|
||||
}
|
||||
|
||||
export function decodeGroundReviewFrame(payload: unknown): GroundReviewFrame {
|
||||
const source = record(payload, "ground review frame");
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user