Добавление канонического графа M4.7
This commit is contained in:
@@ -19,7 +19,11 @@ from .contracts import (
|
||||
)
|
||||
|
||||
REFERENCE_GRAPH_CONFIG_SCHEMA: Final = "missioncore.reference-perception-graph-config/v1"
|
||||
REFERENCE_GRAPH_CONFIG_SCHEMA_V2: Final = "missioncore.reference-perception-graph-config/v2"
|
||||
REFERENCE_GRAPH_STAGE_IDS: Final = frozenset({"detector", "geometry", "temporal", "threat"})
|
||||
REFERENCE_GRAPH_STAGE_IDS_V2: Final = frozenset(
|
||||
{"detector", "geometry", "temporal", "rolling", "threat"}
|
||||
)
|
||||
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
|
||||
_IDENTIFIER = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$")
|
||||
|
||||
@@ -34,9 +38,23 @@ class ProviderRole(StrEnum):
|
||||
GEOMETRY = "geometry"
|
||||
TEMPORAL = "temporal"
|
||||
MOTION = "motion"
|
||||
ROLLING = "rolling"
|
||||
THREAT = "threat"
|
||||
|
||||
|
||||
REFERENCE_GRAPH_PROVIDER_ROLES: Final = frozenset(
|
||||
{
|
||||
ProviderRole.SOURCE,
|
||||
ProviderRole.DETECTOR,
|
||||
ProviderRole.GEOMETRY,
|
||||
ProviderRole.TEMPORAL,
|
||||
ProviderRole.MOTION,
|
||||
ProviderRole.THREAT,
|
||||
}
|
||||
)
|
||||
REFERENCE_GRAPH_PROVIDER_ROLES_V2: Final = frozenset(ProviderRole)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class SourcePacket:
|
||||
"""Execution-only carrier; the graph never interprets opaque sensor payloads."""
|
||||
@@ -211,7 +229,7 @@ class ReferencePerceptionGraphConfig:
|
||||
_identifier(self.graph_id, "graph id")
|
||||
_identifier(self.source_profile_id, "source profile id")
|
||||
roles = [provider.role for provider in self.providers]
|
||||
if len(set(roles)) != len(roles) or set(roles) != set(ProviderRole):
|
||||
if len(set(roles)) != len(roles) or set(roles) != REFERENCE_GRAPH_PROVIDER_ROLES:
|
||||
raise ProviderContractError("graph must pin each provider role exactly once")
|
||||
stage_ids = [queue.stage_id for queue in self.queues]
|
||||
if len(set(stage_ids)) != len(stage_ids):
|
||||
@@ -248,6 +266,57 @@ class ReferencePerceptionGraphConfig:
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ReferencePerceptionGraphConfigV2:
|
||||
"""Final M4 graph contract with explicit retained rolling occupancy."""
|
||||
|
||||
graph_id: str
|
||||
source_profile_id: str
|
||||
providers: tuple[ProviderPin, ...]
|
||||
queues: tuple[QueuePolicy, ...]
|
||||
authority: GraphAuthority = GraphAuthority()
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
_identifier(self.graph_id, "graph id")
|
||||
_identifier(self.source_profile_id, "source profile id")
|
||||
roles = [provider.role for provider in self.providers]
|
||||
if len(set(roles)) != len(roles) or set(roles) != REFERENCE_GRAPH_PROVIDER_ROLES_V2:
|
||||
raise ProviderContractError("graph v2 must pin each provider role exactly once")
|
||||
stage_ids = [queue.stage_id for queue in self.queues]
|
||||
if len(set(stage_ids)) != len(stage_ids):
|
||||
raise ProviderContractError("graph v2 queue policies must be unique")
|
||||
if set(stage_ids) != REFERENCE_GRAPH_STAGE_IDS_V2:
|
||||
raise ProviderContractError("graph v2 must bound each reference stage exactly once")
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return {
|
||||
"schema_version": REFERENCE_GRAPH_CONFIG_SCHEMA_V2,
|
||||
"graph_id": self.graph_id,
|
||||
"source_profile_id": self.source_profile_id,
|
||||
"providers": [provider.to_dict() for provider in self.providers],
|
||||
"queues": [queue.to_dict() for queue in self.queues],
|
||||
"authority": self.authority.to_dict(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, value: object) -> ReferencePerceptionGraphConfigV2:
|
||||
document = _object(value, "reference graph v2 config")
|
||||
_exact_keys(
|
||||
document,
|
||||
{"schema_version", "graph_id", "source_profile_id", "providers", "queues", "authority"},
|
||||
"reference graph v2 config",
|
||||
)
|
||||
if document.get("schema_version") != REFERENCE_GRAPH_CONFIG_SCHEMA_V2:
|
||||
raise ProviderContractError("reference graph v2 config schema is incompatible")
|
||||
return cls(
|
||||
graph_id=_string(document, "graph_id"),
|
||||
source_profile_id=_string(document, "source_profile_id"),
|
||||
providers=tuple(ProviderPin.from_dict(item) for item in _array(document, "providers")),
|
||||
queues=tuple(QueuePolicy.from_dict(item) for item in _array(document, "queues")),
|
||||
authority=GraphAuthority.from_dict(document.get("authority")),
|
||||
)
|
||||
|
||||
|
||||
class SourceProvider(Protocol):
|
||||
provider_id: str
|
||||
|
||||
@@ -290,6 +359,16 @@ class MotionProvider(Protocol):
|
||||
) -> tuple[TemporalObstacle, ...]: ...
|
||||
|
||||
|
||||
class RollingMapProvider(Protocol):
|
||||
provider_id: str
|
||||
|
||||
def update(
|
||||
self,
|
||||
packet: SourcePacket,
|
||||
obstacles: tuple[TemporalObstacle, ...],
|
||||
) -> tuple[TemporalObstacle, ...]: ...
|
||||
|
||||
|
||||
class ThreatProvider(Protocol):
|
||||
provider_id: str
|
||||
|
||||
|
||||
Reference in New Issue
Block a user