spatialdds 0.8.1__tar.gz → 0.8.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spatialdds-0.8.1 → spatialdds-0.8.3}/PKG-INFO +17 -1
- {spatialdds-0.8.1 → spatialdds-0.8.3}/README.md +14 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/pyproject.toml +9 -1
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/__init__.py +2 -2
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/openapi.html +1 -1
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/openapi.yaml +1 -1
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/client.py +69 -7
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/_participant.py +36 -14
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/codec.py +10 -8
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/errors.py +10 -0
- spatialdds-0.8.3/spatialdds/mcp_server.py +1159 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/serve.py +25 -5
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/gateway.py +20 -6
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/gw_http.py +14 -4
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/publish.py +56 -16
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/tenancy.py +3 -1
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/webjson.py +48 -6
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds.egg-info/PKG-INFO +17 -1
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds.egg-info/SOURCES.txt +1 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds.egg-info/entry_points.txt +1 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds.egg-info/requires.txt +3 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/setup.cfg +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/anchors.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/argeo.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/common.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/core.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/discovery.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/events.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/lidar.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/mapping.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/provisional/owm.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/provisional/radio.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/provisional/rf_beam.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/rad.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/semantics.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/slam_frontend.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/types.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/vio.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/idl/vision.idl +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/registry.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/builtin_Time.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_anchors_AnchorDelta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_anchors_AnchorEntry.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetRequest.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetResponse.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_argeo_NodeGeo.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_argeo_QualityRequirements.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_argeo_VpsRequest.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_argeo_VpsResponse.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_common_AssetRef.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_common_FrameRef.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_common_KV.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_common_MetaKV.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_common_Vec2.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_Aabb3.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_BlobChunk.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_BlobRef.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_ComponentRef.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_Edge.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_EntityBinding.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_FrameTransform.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_FramedPose.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_GeoAnchor.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_GeoPose.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_NavSatStatus.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_Node.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_PlannedTrajectory.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_PlannedWaypoint.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_PoseSE3.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_SnapshotRequest.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_SnapshotResponse.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_TileKey.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_TileMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_core_TilePatch.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_Announce.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_Capabilities.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_ContentAnnounce.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_CoverageElement.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_CoverageFilter.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_CoverageHint.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_CoverageQuery.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_CoverageResponse.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_Depart.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_KV.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_ProfileSupport.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_ServiceSummary.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_TopicMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_Transform.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_disco_ValidityWindow.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_events_CrossingLine.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_events_SpatialEvent.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_events_SpatialZone.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_events_ZoneState.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_mapping_Edge.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_mapping_MapAlignment.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_mapping_MapEvent.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_mapping_MapMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_mapping_MapQuality.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_owm_Entity.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_owm_ModelCommand.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_owm_ModelPose.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_semantics_Detection2D.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_semantics_Detection2DSet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_semantics_Detection3D.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_semantics_Detection3DSet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrack.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrackSet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_semantics_Keypoint3D.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_semantics_KeypointLink.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_common_Axis.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameHeader.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameQuality.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_common_Linspace.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_common_ROI.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIReply.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIRequest.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_common_StreamMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetection.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetectionSet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarFrame.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetection.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetectionSet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadSensorMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorFrame.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioObservation.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioScan.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioSensorMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamArraySet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamFrame.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_vision_CamIntrinsics.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_vision_Keypoint2D.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_vision_Track2D.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionDetections.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionFrame.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionMeta.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_slam_frontend_CameraInfo.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_slam_frontend_Feature2D.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_slam_frontend_FeatureMatch.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_slam_frontend_KeyframeFeatures.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_slam_frontend_Landmark.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_slam_frontend_MatchSet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_slam_frontend_TrackObs.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_slam_frontend_Tracklet.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_vio_FusedState.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_vio_ImuInfo.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_vio_ImuSample.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_vio_MagnetometerSample.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/schemas/spatial_vio_SensorFusionSample.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/artifacts/serializer_tables.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/canonical.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/canonjson.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/_extra.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/blob.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/qos.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/record.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/registry.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/_field_aliases.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/builtin/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/builtin/_types.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/anchors/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/anchors/_anchors.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/argeo/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/argeo/_argeo.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/common/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/common/_types.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/core/GnssService/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/core/GnssService/_core.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/core/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/core/_core.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/disco/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/disco/_discovery.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/_events.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/crossing_direction_enum/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/crossing_direction_enum/_events.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/event_state_enum/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/event_state_enum/_events.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/event_type_enum/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/event_type_enum/_events.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/severity_enum/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/severity_enum/_events.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/zone_kind_enum/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/events/enums/zone_kind_enum/_events.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/_mapping.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/alignment_method/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/alignment_method/_mapping.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/edge_type/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/edge_type/_mapping.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/map_event_kind/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/map_event_kind/_mapping.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/map_kind/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/map_kind/_mapping.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/map_status/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/mapping/enums/map_status/_mapping.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/owm/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/owm/_owm.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/semantics/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/semantics/_semantics.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/common/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/common/_common.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/lidar/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/lidar/_lidar.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/rad/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/rad/_rad.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/radio/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/radio/_radio.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/rf_beam/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/rf_beam/_rf_beam.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/vision/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/sensing/vision/_vision.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/slam_frontend/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/slam_frontend/_slam_frontend.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/vio/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/dds/types/spatial/vio/_vio.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/demo/assets/duck.gltf +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/demo/assets/fountain-label.txt +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/demo/assets/fountain-marker.gltf +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/demo/fountain-anchors.mcap +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/demo/manifests/content-fountain-label.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/demo/manifests/content-fountain-marker.json +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/events.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/frames.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/journal.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/recency.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/records.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/__init__.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/bus.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/commands.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/idlmodel.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/limits.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/mcapmeta.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/pin.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/server/xcdr2.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds/tokens.py +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds.egg-info/dependency_links.txt +0 -0
- {spatialdds-0.8.1 → spatialdds-0.8.3}/spatialdds.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialdds
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
4
4
|
Summary: SpatialDDS for programs: a client for the web binding (Appendix N), a native DDS participant, and the reference registry behind one command.
|
|
5
5
|
Author: Open AR Cloud
|
|
6
6
|
License: Apache-2.0
|
|
@@ -25,12 +25,21 @@ Provides-Extra: dds
|
|
|
25
25
|
Requires-Dist: cyclonedds<12,>=11.0.1; extra == "dds"
|
|
26
26
|
Requires-Dist: mcap>=1.1; extra == "dds"
|
|
27
27
|
Requires-Dist: jsonschema>=4.0; extra == "dds"
|
|
28
|
+
Provides-Extra: mcp
|
|
29
|
+
Requires-Dist: mcp<3,>=2.3; extra == "mcp"
|
|
28
30
|
|
|
29
31
|
# spatialdds
|
|
30
32
|
|
|
31
33
|
SpatialDDS for programs: a client over plain HTTPS and a WebSocket, a native
|
|
32
34
|
DDS participant, and the reference registry behind one command.
|
|
33
35
|
|
|
36
|
+
**Integrating with a registry, or with the public sandbox at
|
|
37
|
+
demo.spatialdds.org?** Start with the
|
|
38
|
+
[integrator guide](https://github.com/OpenArCloud/spatialdds-py/blob/main/docs/integrator-guide.md):
|
|
39
|
+
read, watch, and publish content, live entities and claims, and every
|
|
40
|
+
refusal you can meet. Every command in it is run by a gate, in order,
|
|
41
|
+
against the code in the repository.
|
|
42
|
+
|
|
34
43
|
```sh
|
|
35
44
|
pip install spatialdds # the client: two dependencies
|
|
36
45
|
pip install "spatialdds[server]" # and the registry
|
|
@@ -481,6 +490,13 @@ what such a field digests or in what form (spatialdds-py finding 31). Where a
|
|
|
481
490
|
declared value does not parse as a SHA-256 of the asset's bytes, this library
|
|
482
491
|
raises rather than reporting a check it did not perform.
|
|
483
492
|
|
|
493
|
+
One warning, not an error: **`AdvertisedOriginWarning`** (a `UserWarning`),
|
|
494
|
+
once per endpoint, when its search results or resolver metadata advertise
|
|
495
|
+
URLs on another origin than the one you connected to. The library reads
|
|
496
|
+
through your origin either way, so it keeps working; a client that follows
|
|
497
|
+
the advertised URLs reaches the other one, which usually means the endpoint
|
|
498
|
+
was started without `--public-url`. The message names both origins.
|
|
499
|
+
|
|
484
500
|
## Integrity by default
|
|
485
501
|
|
|
486
502
|
Latched reads revalidate with the ETag the endpoint gave; `fetch_asset` checks
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
SpatialDDS for programs: a client over plain HTTPS and a WebSocket, a native
|
|
4
4
|
DDS participant, and the reference registry behind one command.
|
|
5
5
|
|
|
6
|
+
**Integrating with a registry, or with the public sandbox at
|
|
7
|
+
demo.spatialdds.org?** Start with the
|
|
8
|
+
[integrator guide](https://github.com/OpenArCloud/spatialdds-py/blob/main/docs/integrator-guide.md):
|
|
9
|
+
read, watch, and publish content, live entities and claims, and every
|
|
10
|
+
refusal you can meet. Every command in it is run by a gate, in order,
|
|
11
|
+
against the code in the repository.
|
|
12
|
+
|
|
6
13
|
```sh
|
|
7
14
|
pip install spatialdds # the client: two dependencies
|
|
8
15
|
pip install "spatialdds[server]" # and the registry
|
|
@@ -453,6 +460,13 @@ what such a field digests or in what form (spatialdds-py finding 31). Where a
|
|
|
453
460
|
declared value does not parse as a SHA-256 of the asset's bytes, this library
|
|
454
461
|
raises rather than reporting a check it did not perform.
|
|
455
462
|
|
|
463
|
+
One warning, not an error: **`AdvertisedOriginWarning`** (a `UserWarning`),
|
|
464
|
+
once per endpoint, when its search results or resolver metadata advertise
|
|
465
|
+
URLs on another origin than the one you connected to. The library reads
|
|
466
|
+
through your origin either way, so it keeps working; a client that follows
|
|
467
|
+
the advertised URLs reaches the other one, which usually means the endpoint
|
|
468
|
+
was started without `--public-url`. The message names both origins.
|
|
469
|
+
|
|
456
470
|
## Integrity by default
|
|
457
471
|
|
|
458
472
|
Latched reads revalidate with the ETag the endpoint gave; `fetch_asset` checks
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "spatialdds"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.3"
|
|
8
8
|
description = "SpatialDDS for programs: a client for the web binding (Appendix N), a native DDS participant, and the reference registry behind one command."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -40,9 +40,17 @@ server = ["aiohttp>=3.8", "mcap>=1.1", "jsonschema>=4.0"]
|
|
|
40
40
|
# Without this extra `import spatialdds` is unchanged, and `sd.participant`
|
|
41
41
|
# raises `sd.MissingExtra` naming it.
|
|
42
42
|
dds = ["cyclonedds>=11.0.1,<12", "mcap>=1.1", "jsonschema>=4.0"]
|
|
43
|
+
# Being an MCP server over the web binding: `spatialdds-mcp`. The official
|
|
44
|
+
# SDK, and nothing else: the server reads with this package's own client,
|
|
45
|
+
# so it needs no aiohttp, no mcap and no cyclonedds. 2.x because its API is
|
|
46
|
+
# the one written against (1.x's FastMCP is 2.x's MCPServer), which needs
|
|
47
|
+
# Python 3.10 or newer; without this extra the base install is unchanged and
|
|
48
|
+
# `spatialdds-mcp` exits saying which line to run.
|
|
49
|
+
mcp = ["mcp>=2.3,<3"]
|
|
43
50
|
|
|
44
51
|
[project.scripts]
|
|
45
52
|
spatialdds-serve = "spatialdds.serve:main"
|
|
53
|
+
spatialdds-mcp = "spatialdds.mcp_server:main"
|
|
46
54
|
|
|
47
55
|
[project.urls]
|
|
48
56
|
Homepage = "https://github.com/OpenArCloud/spatialdds-py"
|
|
@@ -26,7 +26,7 @@ __all__ = [
|
|
|
26
26
|
"connect", "Endpoint", "Hit", "Topic", "Resolved", "Written",
|
|
27
27
|
"Sample", "Dispose", "EndOfReplay", "Liveliness",
|
|
28
28
|
"SpatialDDSError", "Unreachable", "Problem", "Unauthorized", "SurfaceOff",
|
|
29
|
-
"IntegrityError", "UnverifiableIntegrity",
|
|
29
|
+
"IntegrityError", "UnverifiableIntegrity", "AdvertisedOriginWarning",
|
|
30
30
|
"lift", "SPEC_TAG", "SPEC_COMMIT", "APPENDIX_N", "__version__",
|
|
31
31
|
# The record builders, re-exported so that `sd.circle(...)` reads the way
|
|
32
32
|
# it does in the documentation. They live in `spatialdds.records`.
|
|
@@ -41,7 +41,7 @@ __all__ = [
|
|
|
41
41
|
"participant", "MissingExtra", "dds", "events", "frames", "Recency",
|
|
42
42
|
]
|
|
43
43
|
|
|
44
|
-
__version__ = "0.8.
|
|
44
|
+
__version__ = "0.8.3"
|
|
45
45
|
#: The SpatialDDS-spec commit whose Appendix N and generated artifacts this
|
|
46
46
|
#: package implements. Package versions and specification versions move
|
|
47
47
|
#: independently; this is the one that says what the wire looks like.
|
|
@@ -14,4 +14,4 @@ border-bottom:1px solid var(--line)}p{margin:.35rem 0}.mut,.sum .auth{color:
|
|
|
14
14
|
var(--mut)}.auth{font-size:.85rem;margin-left:.4rem}.v{display:inline-block;
|
|
15
15
|
min-width:4.2rem;font:600 12px ui-monospace,monospace;letter-spacing:.04em;
|
|
16
16
|
color:var(--acc)}.codes{font:12px ui-monospace,monospace;color:var(--mut)}
|
|
17
|
-
.s{margin-right:.5rem}.s4,.s5{color:#b5482c}a{color:var(--acc)}</style></head><body><main><h1>SpatialDDS web binding (spatialdds-py)</h1><p class=mut>Version 0.8.1 · OpenAPI 3.1.0 · generated, never hand-written · <a href='openapi'>openapi.yaml</a></p><p>Generated by <code>tools/gen_openapi.py</code> from the gateway's own tables and the published JSON Schemas; never edited by hand. Reads are open; writes take a bearer token scoped to scenes. The WebSocket streams (N.5) are described on each collection path, not modelled. A deployment's own keyed types (<code>serve --extension</code>) are publishable on their own collection paths, under the same rules as the entity lane when they carry <code>basis</code>, <code>state</code>, <code>state_reason</code> and <code>stamp</code>; a type that names an <code>asserter</code> has it stamped or verified as the bearer token's principal (<code>asserter-not-principal</code>, <code>asserter-ungrounded</code>). Their paths are not listed here, because the deployment chooses them.</p><h2>Operations</h2><section id="search"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/search</code></h3><p class="sum">Services near a geohash (§8.2.3 manifests) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="searchPost"><h3><span class="v post">POST</span> <code>/.well-known/spatialdds/search</code></h3><p class="sum">The same search, as a POST body <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="listSchemas"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/schemas</code></h3><p class="sum">The published JSON Schemas, by type <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="getSchema"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/schemas/{type}</code></h3><p class="sum"> <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="bootstrap"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/bootstrap</code></h3><p class="sum">DDS-join material (N.2); optional <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="resolverMetadata"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/resolver</code></h3><p class="sum">§7.5.2 resolver metadata <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="openapi"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/openapi</code></h3><p class="sum">This document <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="openapiPage"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/openapi.html</code></h3><p class="sum">This document as a static reference page <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="resolve"><h3><span class="v get">GET</span> <code>/spatialdds/resolve</code></h3><p class="sum">Resolve a URI to its manifest (§7.5.2) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">400</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putManifest"><h3><span class="v put">PUT</span> <code>/spatialdds/resolve</code></h3><p class="sum">Publish a manifest, keyed by its own <code>id</code> <span class="auth">token</span></p><p>The <code>uri</code> parameter is not used; the body's <code>id</code> is the key. Its scene segment is the lane. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>).</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawManifest"><h3><span class="v delete">DELETE</span> <code>/spatialdds/resolve</code></h3><p class="sum">Withdraw a manifest; <code>resolve</code> then answers 404 <span class="auth">token</span></p><p>Owner-checked by the URI's scene segment. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>).</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="getAsset"><h3><span class="v get">GET</span> <code>/assets/{name}</code></h3><p class="sum">Asset bytes this endpoint hosts (implementation-defined) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="listAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/announce/v1</code></h3><p class="sum">Every spatial.disco.Announce instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandAnnounce"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/announce/v1</code></h3><p class="sum">An N.6 command addressed through the announce lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">One spatial.disco.Announce instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putAnnounce"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.disco.Announce <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body's key must equal the URL's. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawAnnounce"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.disco.Announce <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection's WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listContentAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/content_announce/v1</code></h3><p class="sum">Every spatial.disco.ContentAnnounce instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandContentAnnounce"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/content_announce/v1</code></h3><p class="sum">An N.6 command addressed through the content_announce lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getContentAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">One spatial.disco.ContentAnnounce instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putContentAnnounce"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.disco.ContentAnnounce <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body's key must equal the URL's. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawContentAnnounce"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.disco.ContentAnnounce <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection's WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listEntity"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/entity/v1</code></h3><p class="sum">Every spatial.owm.Entity instance in the latched set <span class="auth">token</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandEntity"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/entity/v1</code></h3><p class="sum">An N.6 command addressed through the entity lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getEntity"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/entity/v1/{key}</code></h3><p class="sum">One spatial.owm.Entity instance <span class="auth">token</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putEntity"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/entity/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.owm.Entity <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body's key must equal the URL's. <code>If-Match</code> is honoured when present. **Basis gates liveliness.** A DECLARED or AUTHORED body is a declaration and holds until withdrawn; it takes no <code>refresh</code>. An DERIVED or OBSERVED body must declare <code>refresh</code>, and be PUT again within it; when it lapses the gateway withdraws the instance itself, a terminal sample (RETIRED) then the dispose, both with the reason <code>refresh lapsed</code>. On a gateway with a DDS domain the instance is written there too, from the gateway's own participant.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s5">503</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="withdrawEntity"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/entity/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.owm.Entity <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). A terminal sample (RETIRED, with the reason) streams first, then the dispose with its <code>reason</code>, on the collection's WebSocket and, on a gateway with a DDS domain, on the bus.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listGeoAnchor"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/geo_anchor/v1</code></h3><p class="sum">Every spatial.core.GeoAnchor instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandGeoAnchor"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/geo_anchor/v1</code></h3><p class="sum">An N.6 command addressed through the geo_anchor lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getGeoAnchor"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">One spatial.core.GeoAnchor instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putGeoAnchor"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.core.GeoAnchor <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body's key must equal the URL's. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawGeoAnchor"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.core.GeoAnchor <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection's WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listInstances"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/{type}/{version}</code></h3><p class="sum">Any other served topic, read-only <span class="auth">token for operator-scoped</span></p><p>Latched types answer with the latched set; streamed types with the replayed window. Public or operator-scoped per the type: <code>spatial.core.FrameTransform</code> latched public, <code>spatial.events.CrossingLine</code> latched public, <code>spatial.events.SpatialEvent</code> stream bearer, <code>spatial.events.SpatialZone</code> latched public, <code>spatial.owm.ModelPose</code> stream bearer, <code>spatial.semantics.Detection2DSet</code> stream bearer, <code>spatial.semantics.Detection3DSet</code> stream bearer, <code>spatial.semantics.FusedTrackSet</code> stream bearer. **The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listWellKnownInstances"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/{stream}/{type}/{version}</code></h3><p class="sum">A four-segment topic's collection <span class="auth">token for operator-scoped</span></p><p>A four-segment topic (<code>spatialdds/<stream>/<type>/<version></code>, no scene), such as the discovery announce lane, served exactly as a scene-scoped collection is: latched set, <code>ETag</code>, <code>304</code>, the same access classes, and the N.5 stream by upgrading the same path. Read-only: a topic with no scene is in no lane. **The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="getWellKnownInstance"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/{stream}/{type}/{version}/{key}</code></h3><p class="sum">One instance of a four-segment topic <span class="auth">token for operator-scoped</span></p><p>A four-segment topic (<code>spatialdds/<stream>/<type>/<version></code>, no scene), such as the discovery announce lane, served exactly as a scene-scoped collection is: latched set, <code>ETag</code>, <code>304</code>, the same access classes, and the N.5 stream by upgrading the same path. Read-only: a topic with no scene is in no lane. </p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><h2>Schemas</h2><p class=mut>115 components, from the published JSON Schemas at <code>/.well-known/spatialdds/schemas</code>.</p></main></body></html>
|
|
17
|
+
.s{margin-right:.5rem}.s4,.s5{color:#b5482c}a{color:var(--acc)}</style></head><body><main><h1>SpatialDDS web binding (spatialdds-py)</h1><p class=mut>Version 0.8.3 · OpenAPI 3.1.0 · generated, never hand-written · <a href='openapi'>openapi.yaml</a></p><p>Generated by <code>tools/gen_openapi.py</code> from the gateway's own tables and the published JSON Schemas; never edited by hand. Reads are open; writes take a bearer token scoped to scenes. The WebSocket streams (N.5) are described on each collection path, not modelled. A deployment's own keyed types (<code>serve --extension</code>) are publishable on their own collection paths, under the same rules as the entity lane when they carry <code>basis</code>, <code>state</code>, <code>state_reason</code> and <code>stamp</code>; a type that names an <code>asserter</code> has it stamped or verified as the bearer token's principal (<code>asserter-not-principal</code>, <code>asserter-ungrounded</code>). Their paths are not listed here, because the deployment chooses them.</p><h2>Operations</h2><section id="search"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/search</code></h3><p class="sum">Services near a geohash (§8.2.3 manifests) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="searchPost"><h3><span class="v post">POST</span> <code>/.well-known/spatialdds/search</code></h3><p class="sum">The same search, as a POST body <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="listSchemas"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/schemas</code></h3><p class="sum">The published JSON Schemas, by type <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="getSchema"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/schemas/{type}</code></h3><p class="sum"> <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="bootstrap"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/bootstrap</code></h3><p class="sum">DDS-join material (N.2); optional <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="resolverMetadata"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/resolver</code></h3><p class="sum">§7.5.2 resolver metadata <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="openapi"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/openapi</code></h3><p class="sum">This document <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="openapiPage"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/openapi.html</code></h3><p class="sum">This document as a static reference page <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="resolve"><h3><span class="v get">GET</span> <code>/spatialdds/resolve</code></h3><p class="sum">Resolve a URI to its manifest (§7.5.2) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">400</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putManifest"><h3><span class="v put">PUT</span> <code>/spatialdds/resolve</code></h3><p class="sum">Publish a manifest, keyed by its own <code>id</code> <span class="auth">token</span></p><p>The <code>uri</code> parameter is not used; the body's <code>id</code> is the key. Its scene segment is the lane. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>).</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawManifest"><h3><span class="v delete">DELETE</span> <code>/spatialdds/resolve</code></h3><p class="sum">Withdraw a manifest; <code>resolve</code> then answers 404 <span class="auth">token</span></p><p>Owner-checked by the URI's scene segment. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>).</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="getAsset"><h3><span class="v get">GET</span> <code>/assets/{name}</code></h3><p class="sum">Asset bytes this endpoint hosts (implementation-defined) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="listAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/announce/v1</code></h3><p class="sum">Every spatial.disco.Announce instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandAnnounce"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/announce/v1</code></h3><p class="sum">An N.6 command addressed through the announce lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">One spatial.disco.Announce instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putAnnounce"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.disco.Announce <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body's key must equal the URL's. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawAnnounce"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.disco.Announce <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection's WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listContentAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/content_announce/v1</code></h3><p class="sum">Every spatial.disco.ContentAnnounce instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandContentAnnounce"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/content_announce/v1</code></h3><p class="sum">An N.6 command addressed through the content_announce lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getContentAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">One spatial.disco.ContentAnnounce instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putContentAnnounce"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.disco.ContentAnnounce <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body's key must equal the URL's. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawContentAnnounce"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.disco.ContentAnnounce <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection's WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listEntity"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/entity/v1</code></h3><p class="sum">Every spatial.owm.Entity instance in the latched set <span class="auth">token</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandEntity"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/entity/v1</code></h3><p class="sum">An N.6 command addressed through the entity lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getEntity"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/entity/v1/{key}</code></h3><p class="sum">One spatial.owm.Entity instance <span class="auth">token</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putEntity"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/entity/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.owm.Entity <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body's key must equal the URL's. <code>If-Match</code> is honoured when present. **Basis gates liveliness.** A DECLARED or AUTHORED body is a declaration and holds until withdrawn; it takes no <code>refresh</code>. An DERIVED or OBSERVED body must declare <code>refresh</code>, and be PUT again within it; when it lapses the gateway withdraws the instance itself, a terminal sample (RETIRED) then the dispose, both with the reason <code>refresh lapsed</code>. On a gateway with a DDS domain the instance is written there too, from the gateway's own participant.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s5">503</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="withdrawEntity"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/entity/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.owm.Entity <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). A terminal sample (RETIRED, with the reason) streams first, then the dispose with its <code>reason</code>, on the collection's WebSocket and, on a gateway with a DDS domain, on the bus.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listGeoAnchor"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/geo_anchor/v1</code></h3><p class="sum">Every spatial.core.GeoAnchor instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandGeoAnchor"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/geo_anchor/v1</code></h3><p class="sum">An N.6 command addressed through the geo_anchor lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getGeoAnchor"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">One spatial.core.GeoAnchor instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putGeoAnchor"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.core.GeoAnchor <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body's key must equal the URL's. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawGeoAnchor"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.core.GeoAnchor <span class="auth">token</span></p><p>**Writes are per lane.** The path's first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection's WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listInstances"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/{type}/{version}</code></h3><p class="sum">Any other served topic, read-only <span class="auth">token for operator-scoped</span></p><p>Latched types answer with the latched set; streamed types with the replayed window. Public or operator-scoped per the type: <code>spatial.core.FrameTransform</code> latched public, <code>spatial.events.CrossingLine</code> latched public, <code>spatial.events.SpatialEvent</code> stream bearer, <code>spatial.events.SpatialZone</code> latched public, <code>spatial.owm.ModelPose</code> stream bearer, <code>spatial.semantics.Detection2DSet</code> stream bearer, <code>spatial.semantics.Detection3DSet</code> stream bearer, <code>spatial.semantics.FusedTrackSet</code> stream bearer. **The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listWellKnownInstances"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/{stream}/{type}/{version}</code></h3><p class="sum">A four-segment topic's collection <span class="auth">token for operator-scoped</span></p><p>A four-segment topic (<code>spatialdds/<stream>/<type>/<version></code>, no scene), such as the discovery announce lane, served exactly as a scene-scoped collection is: latched set, <code>ETag</code>, <code>304</code>, the same access classes, and the N.5 stream by upgrading the same path. Read-only: a topic with no scene is in no lane. **The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="getWellKnownInstance"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/{stream}/{type}/{version}/{key}</code></h3><p class="sum">One instance of a four-segment topic <span class="auth">token for operator-scoped</span></p><p>A four-segment topic (<code>spatialdds/<stream>/<type>/<version></code>, no scene), such as the discovery announce lane, served exactly as a scene-scoped collection is: latched set, <code>ETag</code>, <code>304</code>, the same access classes, and the N.5 stream by upgrading the same path. Read-only: a topic with no scene is in no lane. </p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><h2>Schemas</h2><p class=mut>115 components, from the published JSON Schemas at <code>/.well-known/spatialdds/schemas</code>.</p></main></body></html>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
openapi: 3.1.0
|
|
3
3
|
info:
|
|
4
4
|
title: SpatialDDS web binding (spatialdds-py)
|
|
5
|
-
version: 0.8.
|
|
5
|
+
version: 0.8.3
|
|
6
6
|
summary: SpatialDDS 1.8 Appendix N over HTTPS, plus this endpoint's write surface.
|
|
7
7
|
description: Generated by `tools/gen_openapi.py` from the gateway's own tables and the published JSON Schemas; never edited by hand. Reads are open; writes take a bearer token scoped to scenes. The WebSocket streams (N.5) are described on each collection path, not modelled. A deployment's own keyed types (`serve --extension`) are publishable on their own collection paths, under the same rules as the entity lane when they carry `basis`, `state`, `state_reason` and `stamp`; a type that names an `asserter` has it stamped or verified as the bearer token's principal (`asserter-not-principal`, `asserter-ungrounded`). Their paths are not listed here, because the deployment chooses them.
|
|
8
8
|
servers:
|
|
@@ -11,6 +11,7 @@ import hashlib
|
|
|
11
11
|
import json
|
|
12
12
|
import math
|
|
13
13
|
import re
|
|
14
|
+
import warnings
|
|
14
15
|
from dataclasses import dataclass
|
|
15
16
|
from typing import Any, Dict, Iterator, List, Optional
|
|
16
17
|
|
|
@@ -19,7 +20,7 @@ import requests
|
|
|
19
20
|
from . import canonical, records
|
|
20
21
|
from .errors import (IntegrityError, Problem, SpatialDDSError, Unreachable,
|
|
21
22
|
UnverifiableIntegrity, problem_for)
|
|
22
|
-
from .errors import SurfaceOff, Unauthorized
|
|
23
|
+
from .errors import AdvertisedOriginWarning, SurfaceOff, Unauthorized
|
|
23
24
|
|
|
24
25
|
#: What the package's facade resolves through this module. The two exception
|
|
25
26
|
#: subclasses are here because this is the module `spatialdds.Unauthorized`
|
|
@@ -29,11 +30,15 @@ __all__ = [
|
|
|
29
30
|
"connect", "Endpoint", "Hit", "Topic", "Resolved", "Written",
|
|
30
31
|
"Sample", "Dispose", "EndOfReplay", "Liveliness",
|
|
31
32
|
"SpatialDDSError", "Unreachable", "Problem", "Unauthorized", "SurfaceOff",
|
|
32
|
-
"IntegrityError", "UnverifiableIntegrity", "
|
|
33
|
+
"IntegrityError", "UnverifiableIntegrity", "AdvertisedOriginWarning",
|
|
34
|
+
"lift",
|
|
33
35
|
]
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
WELL_KNOWN = "/.well-known/spatialdds"
|
|
39
|
+
#: How many search pages `Endpoint.search` follows before it gives up
|
|
40
|
+
#: rather than answer with part of the list.
|
|
41
|
+
SEARCH_PAGES = 100
|
|
37
42
|
_SHA256 = re.compile(r"^(?:sha256:)?([0-9a-f]{64})$", re.I)
|
|
38
43
|
lift = canonical.lift
|
|
39
44
|
|
|
@@ -112,6 +117,7 @@ class Endpoint:
|
|
|
112
117
|
self._cache: Dict[str, Any] = {}
|
|
113
118
|
self._authority: Optional[str] = authority
|
|
114
119
|
self._s = requests.Session()
|
|
120
|
+
self._origin_warned = False
|
|
115
121
|
|
|
116
122
|
# -- plumbing --
|
|
117
123
|
def _url(self, path_or_url: str) -> str:
|
|
@@ -124,6 +130,28 @@ class Endpoint:
|
|
|
124
130
|
scheme, _, rest = url.partition("://")
|
|
125
131
|
return f"{scheme.lower()}://{rest.split('/', 1)[0].lower()}"
|
|
126
132
|
|
|
133
|
+
def _advertised(self, url: str, where: str) -> None:
|
|
134
|
+
"""Warn, once for this endpoint, if `url` is on another origin.
|
|
135
|
+
|
|
136
|
+
The URLs a service manifest or the resolver metadata advertise are
|
|
137
|
+
this endpoint's own, so their origin should be the one they were
|
|
138
|
+
read from. The library reads through `base` regardless (`_path_of`),
|
|
139
|
+
which is why a misadvertised endpoint read fine through it and
|
|
140
|
+
nowhere else (finding 83); this says so instead of hiding it.
|
|
141
|
+
"""
|
|
142
|
+
if self._origin_warned or not url.startswith(("http://", "https://")):
|
|
143
|
+
return
|
|
144
|
+
here, there = _norm_origin(self.base), _norm_origin(url)
|
|
145
|
+
if here == there:
|
|
146
|
+
return
|
|
147
|
+
self._origin_warned = True
|
|
148
|
+
warnings.warn(AdvertisedOriginWarning(
|
|
149
|
+
f"{here} advertises {there} in its {where}, not the origin it "
|
|
150
|
+
f"was read from. This client reads through {here}; a client "
|
|
151
|
+
f"that follows the advertised URL reaches {there}. The endpoint "
|
|
152
|
+
f"is likely missing --public-url (or --authority)."),
|
|
153
|
+
stacklevel=3)
|
|
154
|
+
|
|
127
155
|
def _headers(self, extra: Optional[Dict[str, str]] = None,
|
|
128
156
|
url: Optional[str] = None) -> Dict[str, str]:
|
|
129
157
|
"""Request headers; the token only for this endpoint's own origin.
|
|
@@ -245,16 +273,39 @@ class Endpoint:
|
|
|
245
273
|
|
|
246
274
|
# -- discovery --
|
|
247
275
|
def search(self, geohash: Optional[str] = None,
|
|
248
|
-
kinds: Optional[List[str]] = None
|
|
249
|
-
|
|
276
|
+
kinds: Optional[List[str]] = None,
|
|
277
|
+
max_pages: int = SEARCH_PAGES) -> List["Hit"]:
|
|
278
|
+
"""§3.3.0 search. One cell in, service manifests out.
|
|
279
|
+
|
|
280
|
+
Every page: a response carrying a `next_page_token` is followed with
|
|
281
|
+
`page_token`, the same query each time, until one carries none. 0.8.2
|
|
282
|
+
and earlier read only the first page, so a paging endpoint's later
|
|
283
|
+
services were silently missing (finding 82). An endpoint still paging
|
|
284
|
+
after `max_pages` pages raises rather than answering with part of
|
|
285
|
+
the list.
|
|
286
|
+
"""
|
|
250
287
|
params: Dict[str, Any] = {}
|
|
251
288
|
if geohash:
|
|
252
289
|
params["geohash"] = geohash
|
|
253
290
|
if kinds:
|
|
254
291
|
params["kind"] = ",".join(kinds)
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
292
|
+
hits: List["Hit"] = []
|
|
293
|
+
token = ""
|
|
294
|
+
for _ in range(max_pages):
|
|
295
|
+
page = dict(params, page_token=token) if token else params
|
|
296
|
+
doc = self.get_json(f"{WELL_KNOWN}/search", params=page or None,
|
|
297
|
+
revalidate=False)
|
|
298
|
+
for m in doc.get("results", []):
|
|
299
|
+
for t in (m.get("service") or {}).get("topics") or []:
|
|
300
|
+
if isinstance(t, dict) and t.get("url"):
|
|
301
|
+
self._advertised(str(t["url"]), "search results")
|
|
302
|
+
hits.append(Hit(self, m))
|
|
303
|
+
token = doc.get("next_page_token") or ""
|
|
304
|
+
if not token:
|
|
305
|
+
return hits
|
|
306
|
+
raise SpatialDDSError(
|
|
307
|
+
f"{self.base}: search was still paging after {max_pages} pages "
|
|
308
|
+
f"({len(hits)} services read); pass max_pages= to read further")
|
|
258
309
|
|
|
259
310
|
def near(self, lat: float, lon: float, radius_m: float) -> List["Hit"]:
|
|
260
311
|
"""Services whose declared coverage reaches a point.
|
|
@@ -287,6 +338,7 @@ class Endpoint:
|
|
|
287
338
|
raise SpatialDDSError(
|
|
288
339
|
f"{self.base} publishes no resolver metadata, so it resolves "
|
|
289
340
|
f"nothing. It is not an authority for any spatialdds:// URI.")
|
|
341
|
+
self._advertised(str(hb), "resolver metadata")
|
|
290
342
|
path = hb if hb.startswith(self.base) else self.base + _path_of(hb)
|
|
291
343
|
r = self._request("GET", path, params={"uri": uri}, raw=True)
|
|
292
344
|
return Resolved(self, uri, r.json(), r.headers.get("ETag", ""))
|
|
@@ -735,6 +787,16 @@ def _elements(cov: Any) -> List[Dict[str, Any]]:
|
|
|
735
787
|
|
|
736
788
|
# ---- small helpers --------------------------------------------------------
|
|
737
789
|
|
|
790
|
+
def _norm_origin(url: str) -> str:
|
|
791
|
+
"""scheme://host[:port], lowercased, with a scheme's default port dropped."""
|
|
792
|
+
scheme, _, rest = url.partition("://")
|
|
793
|
+
scheme, host = scheme.lower(), rest.split("/", 1)[0].lower()
|
|
794
|
+
default = {"http": ":80", "https": ":443"}.get(scheme)
|
|
795
|
+
if default and host.endswith(default):
|
|
796
|
+
host = host[:-len(default)]
|
|
797
|
+
return f"{scheme}://{host}"
|
|
798
|
+
|
|
799
|
+
|
|
738
800
|
def _path_of(url: str) -> str:
|
|
739
801
|
if url.startswith(("http://", "https://")):
|
|
740
802
|
rest = url.split("//", 1)[1]
|
|
@@ -75,6 +75,9 @@ DOMAIN_ENV = "SPATIALDDS_DDS_DOMAIN"
|
|
|
75
75
|
#: program can make: a robot still exists after its bridge stops; a
|
|
76
76
|
#: withdrawn catalogue item does not.
|
|
77
77
|
ON_EXIT = ("keep", "withdraw")
|
|
78
|
+
#: The most samples one `Reader.take()` takes from its DataReader; the rest
|
|
79
|
+
#: wait for the next call (finding 76).
|
|
80
|
+
TAKE_LIMIT = 256
|
|
78
81
|
#: Message types whose well-known writers the participant owns: it announces
|
|
79
82
|
#: and departs on a program's behalf, so a second writer would be a second
|
|
80
83
|
#: claim about the same presence.
|
|
@@ -228,10 +231,10 @@ class Writer:
|
|
|
228
231
|
"""One lane's writer. Created by `Participant.writer`, never directly."""
|
|
229
232
|
|
|
230
233
|
def __init__(self, p: "Participant", lane: _Lane, on_exit: Optional[str],
|
|
231
|
-
floor_hz: Optional[float],
|
|
234
|
+
floor_hz: Optional[float],
|
|
232
235
|
lifespan_sec: Optional[float]) -> None:
|
|
233
236
|
self._p, self._lane = p, lane
|
|
234
|
-
self.on_exit, self.floor_hz
|
|
237
|
+
self.on_exit, self.floor_hz = on_exit, floor_hz
|
|
235
238
|
self._dw = DataWriter(p._dp, p._topic(lane),
|
|
236
239
|
qos=writer_qos(lane.profile, lifespan_sec))
|
|
237
240
|
self._last: Dict[str, Any] = {}
|
|
@@ -286,13 +289,15 @@ class Writer:
|
|
|
286
289
|
def write(self, sample: Any) -> Any:
|
|
287
290
|
"""Write a typed sample, or its N.1 document. Returns the typed sample.
|
|
288
291
|
|
|
289
|
-
Validated against the published schema first
|
|
290
|
-
|
|
291
|
-
and
|
|
292
|
+
Validated against the published schema first, always: an invalid
|
|
293
|
+
sample raises `SchemaInvalid` and nothing reaches the bus. There is
|
|
294
|
+
no unvalidated mode. 0.8.2 and earlier had one, `validate=False`,
|
|
295
|
+
because validation compiled the schema per sample (finding 75); it
|
|
296
|
+
is compiled once a type now, and an unvalidated write is a hole in
|
|
297
|
+
the write contract, not a fast path.
|
|
292
298
|
"""
|
|
293
299
|
sample = self._coerce(sample)
|
|
294
|
-
|
|
295
|
-
codec.validate(sample)
|
|
300
|
+
codec.validate(sample)
|
|
296
301
|
key = codec.key_of(sample)
|
|
297
302
|
with self._lock:
|
|
298
303
|
self._dw.write(sample)
|
|
@@ -336,6 +341,12 @@ class Writer:
|
|
|
336
341
|
its terminal state itself, and this wrote it again (S18). The members
|
|
337
342
|
this method sets (`state`, `state_reason`, `stamp`) and the key are
|
|
338
343
|
not changes a caller can make.
|
|
344
|
+
|
|
345
|
+
The terminal sample's `stamp` is this host's wall clock at the moment
|
|
346
|
+
of withdrawal, whatever clock the instance's other samples carried;
|
|
347
|
+
`on_exit="withdraw"` stamps the same way. A writer on another clock (a
|
|
348
|
+
simulator, a replay, a device with its own time base) cannot set it,
|
|
349
|
+
so its withdrawals read as happening at host time (finding 74).
|
|
339
350
|
"""
|
|
340
351
|
if not isinstance(reason, str) or not reason.strip():
|
|
341
352
|
raise ValueError("withdraw() needs a non-empty reason=: a removal "
|
|
@@ -343,8 +354,12 @@ class Writer:
|
|
|
343
354
|
fixed = {"state", "state_reason", "stamp", codec.key_member(self._lane.fqn)}
|
|
344
355
|
refused = sorted(set(changes) & fixed)
|
|
345
356
|
if refused:
|
|
346
|
-
|
|
347
|
-
|
|
357
|
+
clock = (" (the terminal stamp is this host's wall clock at "
|
|
358
|
+
"withdrawal, and a caller cannot set it)"
|
|
359
|
+
if "stamp" in refused else "")
|
|
360
|
+
raise ValueError(f"withdraw() sets {', '.join(refused)} itself"
|
|
361
|
+
f"{clock}; pass reason=, and the other members "
|
|
362
|
+
f"to change")
|
|
348
363
|
with self._lock:
|
|
349
364
|
if key not in self._last:
|
|
350
365
|
raise KeyError(f"{self.topic} holds no instance keyed {key!r} "
|
|
@@ -357,7 +372,7 @@ class Writer:
|
|
|
357
372
|
f"{', '.join(unknown)}")
|
|
358
373
|
terminal = self._terminal(dataclasses.replace(last, **changes),
|
|
359
374
|
reason)
|
|
360
|
-
if changes
|
|
375
|
+
if changes:
|
|
361
376
|
codec.validate(terminal)
|
|
362
377
|
del self._last[key]
|
|
363
378
|
# Immediately: nothing of this writer's may come between the
|
|
@@ -514,7 +529,7 @@ class Reader:
|
|
|
514
529
|
|
|
515
530
|
def _events(self) -> List[Any]:
|
|
516
531
|
out: List[Any] = []
|
|
517
|
-
for s in self._dr.take(N=
|
|
532
|
+
for s in self._dr.take(N=TAKE_LIMIT) or []:
|
|
518
533
|
info = getattr(s, "sample_info", None)
|
|
519
534
|
if info is None:
|
|
520
535
|
continue
|
|
@@ -534,7 +549,14 @@ class Reader:
|
|
|
534
549
|
return out
|
|
535
550
|
|
|
536
551
|
def take(self) -> List[Any]:
|
|
537
|
-
"""
|
|
552
|
+
"""What has arrived, now, without blocking: up to 256 samples' events.
|
|
553
|
+
|
|
554
|
+
A call takes at most `TAKE_LIMIT` samples from the reader, and any
|
|
555
|
+
beyond that wait for the next call. Nothing in what it returns says
|
|
556
|
+
more are waiting, so a program whose lanes can deliver more than 256
|
|
557
|
+
samples between two calls should call `take()` again until it
|
|
558
|
+
returns no events (finding 76). `events()` keeps calling it and has
|
|
559
|
+
no such bound.
|
|
538
560
|
|
|
539
561
|
For a program that already owns its loop: a tick, a framework's
|
|
540
562
|
timer, a request handler. The events are the ones `events()` yields,
|
|
@@ -742,7 +764,7 @@ class Participant:
|
|
|
742
764
|
def writer(self, type_: Union[str, type], *, stream: Optional[str] = None,
|
|
743
765
|
qos_profile: Union[str, Profile, None] = None,
|
|
744
766
|
on_exit: Optional[str] = None,
|
|
745
|
-
floor_hz: Optional[float] = None,
|
|
767
|
+
floor_hz: Optional[float] = None,
|
|
746
768
|
version: str = "v1", type_name: Optional[str] = None,
|
|
747
769
|
lifespan_sec: Optional[float] = None,
|
|
748
770
|
topic: Optional[str] = None) -> Writer:
|
|
@@ -790,7 +812,7 @@ class Participant:
|
|
|
790
812
|
f"a writer does not offer a contract it will not meet")
|
|
791
813
|
if problems:
|
|
792
814
|
raise ParticipantRefused(problems)
|
|
793
|
-
w = Writer(self, lane, on_exit, floor_hz,
|
|
815
|
+
w = Writer(self, lane, on_exit, floor_hz, lifespan_sec)
|
|
794
816
|
self._writers.append(w)
|
|
795
817
|
for sid, svc in self._services.items():
|
|
796
818
|
if svc["spec"]["topics"] is None:
|
|
@@ -363,7 +363,7 @@ def validate(sample: Any) -> Dict[str, Any]:
|
|
|
363
363
|
check stops at the second: representable in N.1.
|
|
364
364
|
"""
|
|
365
365
|
try:
|
|
366
|
-
import jsonschema
|
|
366
|
+
import jsonschema # noqa: F401
|
|
367
367
|
except ImportError as e:
|
|
368
368
|
from ._extra import EXTRA, MissingExtra
|
|
369
369
|
raise MissingExtra(f"validation needs jsonschema: {EXTRA}") from e
|
|
@@ -371,14 +371,16 @@ def validate(sample: Any) -> Dict[str, Any]:
|
|
|
371
371
|
fqn = fqn_of(sample)
|
|
372
372
|
if fqn in _EXT_FQN:
|
|
373
373
|
return doc
|
|
374
|
-
|
|
375
|
-
|
|
374
|
+
# Compiled once a type, a process (finding 75): a validated pose write
|
|
375
|
+
# cost 5-7 ms when this compiled the schema, and read every vendored
|
|
376
|
+
# schema from disk, per sample.
|
|
377
|
+
v = webjson.vendored_validator(fqn)
|
|
378
|
+
if v is None:
|
|
376
379
|
raise SchemaInvalid(f"no published schema for {fqn}, so a sample "
|
|
377
|
-
f"cannot be validated
|
|
378
|
-
f"
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
except jsonschema.ValidationError as e:
|
|
380
|
+
f"cannot be validated, and nothing is written "
|
|
381
|
+
f"unvalidated")
|
|
382
|
+
e = webjson.first_error(v, doc)
|
|
383
|
+
if e is not None:
|
|
382
384
|
where = "/".join(str(p) for p in e.absolute_path) or "(root)"
|
|
383
385
|
raise SchemaInvalid(f"{fqn} at {where}: {e.message}") from e
|
|
384
386
|
return doc
|
|
@@ -85,3 +85,13 @@ def problem_for(status: int, doc: Dict[str, Any], url: str,
|
|
|
85
85
|
if status == 405 and slug == "publish-disabled":
|
|
86
86
|
return SurfaceOff(status, doc, url)
|
|
87
87
|
return Problem(status, doc, url)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AdvertisedOriginWarning(UserWarning):
|
|
91
|
+
"""An endpoint advertises URLs on an origin other than the one it was
|
|
92
|
+
read from (finding 83).
|
|
93
|
+
|
|
94
|
+
The library reads through the origin it connected to, so it works either
|
|
95
|
+
way; a client that follows the advertised URL, as N.3's hypermedia
|
|
96
|
+
intends, reaches the other origin. Warned once per endpoint, naming both.
|
|
97
|
+
"""
|