diff --git a/config/default.yaml b/config/default.yaml index e28202ac..34db4b12 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -24,29 +24,13 @@ opensky: client_id: "your-client-id" client_secret: "your-client-secret" -# Air traffic data configuration +# Default air traffic simulator settings (can be overridden per step in scenario YAML) air_traffic_simulator_settings: number_of_aircraft: 3 simulation_duration: 10 single_or_multiple_sensors: "single" # this setting specifies if the traffic data is submitted from a single sensor or multiple sensors sensor_ids: ["a0b7d47e5eac45dc8cbaf47e6fe0e558"] # List of sensor IDs to use when 'multiple' is selected - session_ids: ["ee9405e564ea4373823e37d950858e6a"] # List of session IDs to use when 'multiple' is selected, a session id is needed in Flight Blender to depict a period of time these observations were made (this assumes the observations may not be continuous); if empty, random UUIDs will be generated - -# Bluesky Air traffic data configuration -blue_sky_air_traffic_simulator_settings: - number_of_aircraft: 3 - simulation_duration: 30 - single_or_multiple_sensors: "single" # this setting specifies if the traffic data is submitted from a single sensor or multiple sensors - sensor_ids: ["562e6297036a4adebb4848afcd1ede90"] # List of sensor IDs to use when 'multiple' is selected - session_ids: ["ee9405e564ea4373823e37d950858e6a"] # List of session IDs to use when 'multiple' is selected, a session id is needed in Flight Blender to depict a period of time these observations were made (this assumes the observations may not be continuous); if empty, random UUIDs will be generated - -# Bayesian Air traffic data configuration -bayesian_air_traffic_simulator_settings: - number_of_aircraft: 3 - simulation_duration: 30 - single_or_multiple_sensors: "single" # this setting specifies if the traffic data is submitted from a single sensor or multiple sensors - sensor_ids: ["562e6297036a4adebb4848afcd1ede90"] # List of sensor IDs to use when 'multiple' is selected - session_ids: ["ee9405e564ea4373823e37d950858e6a"] # List of session IDs to use when 'multiple' is selected, a session id is needed in Flight Blender to depict a period of time these observations were made (this assumes the observations may not be continuous); if empty, random UUIDs will be generated + session_ids: ["ee9405e564ea4373823e37d950858e6a"] # List of session IDs to use when 'multiple' is selected # AMQP/RabbitMQ configuration for event monitoring # Set AMQP_URL environment variable or configure here @@ -84,7 +68,6 @@ suites: air_traffic_simulations: scenarios: - name: openutm_sim_air_traffic_data - - name: stream_air_traffic_example - name: opensky_live_data - name: bayesian_sim_air_traffic_data - name: bluesky_sim_air_traffic_data diff --git a/config/pull_request.yaml b/config/pull_request.yaml index ded32bb6..9286b87e 100644 --- a/config/pull_request.yaml +++ b/config/pull_request.yaml @@ -18,27 +18,12 @@ opensky: client_id: "your-client-id" client_secret: "your-client-secret" -# Air traffic data configuration +# Default air traffic simulator settings (can be overridden per step in scenario YAML) air_traffic_simulator_settings: number_of_aircraft: 3 simulation_duration: 10 - single_or_multiple_sensors: "multiple" # this setting specifiies if the traffic data is submitted from a single sensor or multiple sensors - sensor_ids: ["a0b7d47e5eac45dc8cbaf47e6fe0e558"] # List of sensor IDs to use when 'multiple' is selected - -# Bluesky Air traffic data configuration -blue_sky_air_traffic_simulator_settings: - number_of_aircraft: 3 - simulation_duration: 30 - single_or_multiple_sensors: "multiple" # this setting specifiies if the traffic data is submitted from a single sensor or multiple sensors - sensor_ids: ["562e6297036a4adebb4848afcd1ede90"] # List of sensor IDs to use when 'multiple' is selected - -# Bayesian Air traffic data configuration -bayesian_air_traffic_simulator_settings: - number_of_aircraft: 3 - simulation_duration: 30 single_or_multiple_sensors: "multiple" # this setting specifies if the traffic data is submitted from a single sensor or multiple sensors - sensor_ids: ["562e6297036a4adebb4848afcd1ede90"] # List of sensor IDs to use when 'multiple' is selected - session_ids: ["ee9405e564ea4373823e37d950858e6a"] # List of session IDs to use when 'multiple' is selected, a session id is needed in Flight Blender to depict a period of time these observations were made (this assumes the observations may not be continuous); if empty, random UUIDs will be generated + sensor_ids: ["a0b7d47e5eac45dc8cbaf47e6fe0e558"] # List of sensor IDs to use when 'multiple' is selected data_files: trajectory: "config/bern/trajectory_f1.json" # Path to flight declarations JSON file diff --git a/docs/daa_scenario_authoring_guide.md b/docs/daa_scenario_authoring_guide.md index 2daa325e..7861647a 100644 --- a/docs/daa_scenario_authoring_guide.md +++ b/docs/daa_scenario_authoring_guide.md @@ -165,13 +165,12 @@ steps: arguments: state: ACTIVATED - id: generate_traffic - step: Generate BlueSky Simulation Air Traffic Data + step: Stream Air Traffic arguments: + provider: bluesky config_path: config/edmonton/bluesky_.scn duration: 130 # > encounter window - - step: Submit Simulated Air Traffic - arguments: - observations: ${{ steps.generate_traffic.result }} + target: flight_blender background: true - step: Wait X seconds arguments: @@ -186,7 +185,7 @@ steps: state: ENDED needs: - Submit Telemetry - - Submit Simulated Air Traffic + - generate_traffic - step: Stop AMQP Queue Monitor needs: - update_state_ended @@ -256,13 +255,12 @@ steps: # Intruder traffic - id: generate_traffic - step: Generate BlueSky Simulation Air Traffic Data + step: Stream Air Traffic arguments: + provider: bluesky config_path: config/edmonton/bluesky_.scn duration: 210 - - step: Submit Simulated Air Traffic - arguments: - observations: ${{ steps.generate_traffic.result }} + target: flight_blender background: true - step: Wait X seconds arguments: @@ -302,13 +300,13 @@ steps: arguments: declaration_id: ${{ steps.setup_declarations.result.declarations[0].id }} state: ENDED - needs: [submit_alpha_telemetry, submit_bravo_telemetry, Submit Simulated Air Traffic] + needs: [submit_alpha_telemetry, submit_bravo_telemetry, generate_traffic] - id: end_bravo step: Update Operation State of declaration arguments: declaration_id: ${{ steps.setup_declarations.result.declarations[1].id }} state: ENDED - needs: [submit_alpha_telemetry, submit_bravo_telemetry, Submit Simulated Air Traffic] + needs: [submit_alpha_telemetry, submit_bravo_telemetry, generate_traffic] - step: Stop AMQP Queue Monitor needs: [end_alpha, end_bravo] diff --git a/docs/scenarios/airtraffic-simulations/openutm_sim_air_traffic_data.md b/docs/scenarios/airtraffic-simulations/openutm_sim_air_traffic_data.md index 36db4d5f..3b228bed 100644 --- a/docs/scenarios/airtraffic-simulations/openutm_sim_air_traffic_data.md +++ b/docs/scenarios/airtraffic-simulations/openutm_sim_air_traffic_data.md @@ -9,15 +9,12 @@ The scenario executes the following sequence of steps: -### 1. Generate Simulated Air Traffic -- **Step:** `AirTrafficClient.generate_simulated_air_traffic_data` -- **Action:** Generates a set of simulated flight observations (e.g., positions, velocities) representing local air traffic. -- **Context:** Unlike live tracking, this uses internal simulation logic/templates to create predictable traffic patterns for testing. +### 1. Stream Air Traffic +- **Step:** `Stream Air Traffic` +- **Arguments:** `provider: geojson`, `target: flight_blender` +- **Action:** Generates simulated flight observations using the GeoJSON provider and submits them to Flight Blender's air traffic data feed. +- **Context:** Uses internal simulation logic/templates to create predictable traffic patterns for testing. +- **Verification:** Ensures observations are generated and the API accepts the payload. -### 2. Submit Traffic Data -- **Step:** `FlightBlenderClient.submit_simulated_air_traffic` -- **Action:** Submits the generated observations to Flight Blender's air traffic data feed. -- **Verification:** Ensures the API accepts the payload (typically a bulk submission of observations). - -### 3. Completion +### 2. Completion - **Action:** The scenario completes successfully upon meaningful submission callback. diff --git a/docs/scenarios/sdsp-f3623/sdsp_track.md b/docs/scenarios/sdsp-f3623/sdsp_track.md index d5824f08..87911bcf 100644 --- a/docs/scenarios/sdsp-f3623/sdsp_track.md +++ b/docs/scenarios/sdsp-f3623/sdsp_track.md @@ -14,11 +14,10 @@ The scenario executes the following sequence of steps: - **Action:** Initiates a new SDSP session with a unique UUID. - **Context:** Signals the beginning of a data provision session. -### 2. Generate and Submit Air Traffic -- **Step:** `Generate Simulated Air Traffic Data` -- **Action:** Generates simulated flight observations. -- **Step:** `Submit Simulated Air Traffic` (Background Task) -- **Action:** Starts submitting the generated observations to Flight Blender in a background process. +### 2. Stream Air Traffic +- **Step:** `Stream Air Traffic` (Background Task) +- **Arguments:** `provider: geojson`, `target: flight_blender` +- **Action:** Generates simulated flight observations and submits them to Flight Blender in a background process. - **Context:** Simulates a live feed of aircraft tracking data being pushed to the system. ### 3. Initial Wait diff --git a/docs/scenarios/sdsp-f3623/verify_sdsp_metrics.md b/docs/scenarios/sdsp-f3623/verify_sdsp_metrics.md index 8786bf56..aae1473a 100644 --- a/docs/scenarios/sdsp-f3623/verify_sdsp_metrics.md +++ b/docs/scenarios/sdsp-f3623/verify_sdsp_metrics.md @@ -8,12 +8,10 @@ ## Execution Flow The scenario executes the following sequence of steps: -1. **Generate Bayesian Simulation Air Traffic Data** - Create synthetic air traffic data streams that mimic real-world SDSP inputs, including various flight parameters and metadata. -2. **Fetch Session IDs for Bayesian Simulation** - Retrieve the session identifiers for the generated air traffic data to track and correlate metrics accurately throughout the test. -3. **Submit Simulated Air Traffic** - Ingest the generated air traffic data into Flight Blender, ensuring that the system processes the data as it would in a live environment. -4. **Start SDSP Session** - Initiate and terminate SDSP sessions to simulate real-world usage patterns, allowing for the measurement of session-related metrics such as active session count and data volume. -5. **Verify Reported Metrics in Flight Blender** - Check the Flight Blender dashboard and logs to confirm that the metrics for active SDSP sessions, data volume, and update frequency are accurately reported based on the actions performed in the previous steps. -5. **Stop SDSP Session** - Terminate the SDSP sessions to ensure that the system correctly updates the metrics to reflect the cessation of data streams and session activity. +1. **Stream Air Traffic** (`provider: bayesian`, `target: flight_blender`) - Generate synthetic air traffic data using the Bayesian provider and submit to Flight Blender, mimicking real-world SDSP inputs including various flight parameters and metadata. +2. **Start SDSP Session** - Initiate and terminate SDSP sessions to simulate real-world usage patterns, allowing for the measurement of session-related metrics such as active session count and data volume. +3. **Verify Reported Metrics in Flight Blender** - Check the Flight Blender dashboard and logs to confirm that the metrics for active SDSP sessions, data volume, and update frequency are accurately reported based on the actions performed in the previous steps. +4. **Stop SDSP Session** - Terminate the SDSP sessions to ensure that the system correctly updates the metrics to reflect the cessation of data streams and session activity. ## Expected Outcomes diff --git a/scenarios/README.md b/scenarios/README.md index db3a03f9..896ad08c 100644 --- a/scenarios/README.md +++ b/scenarios/README.md @@ -53,31 +53,14 @@ Use the optional `groups` section to define named step collections: ```yaml name: opensky_live_data -description: Fetch live flight data and submit to Flight Blender. - -groups: - fetch_and_submit_opensky: - description: Fetches OpenSky data and submits it to Flight Blender - steps: - - id: fetch - step: Fetch OpenSky Data - - - id: submit - step: Submit Air Traffic - arguments: - observations: ${{ group.fetch.result }} - - - id: wait - step: Wait X seconds - arguments: - duration: 3 +description: Fetch live flight data from OpenSky and submit to Flight Blender. steps: - # Execute the group once - - step: fetch_and_submit_opensky - - # Execute the group in a loop - - step: fetch_and_submit_opensky + - step: Stream Air Traffic + id: stream_opensky + arguments: + provider: opensky + target: flight_blender loop: count: 5 ``` diff --git a/scenarios/airtraffic-simulations/opensky_live_data.yaml b/scenarios/airtraffic-simulations/opensky_live_data.yaml index ed4fd709..255880f9 100644 --- a/scenarios/airtraffic-simulations/opensky_live_data.yaml +++ b/scenarios/airtraffic-simulations/opensky_live_data.yaml @@ -7,5 +7,6 @@ steps: arguments: provider: opensky target: flight_blender + duration: 10 loop: - count: 5 + count: 2 diff --git a/scenarios/airtraffic-simulations/stream_air_traffic_example.yaml b/scenarios/airtraffic-simulations/stream_air_traffic_example.yaml deleted file mode 100644 index b7fde406..00000000 --- a/scenarios/airtraffic-simulations/stream_air_traffic_example.yaml +++ /dev/null @@ -1,53 +0,0 @@ -name: stream_air_traffic_example -version: "1.0" -description: | - Example scenario demonstrating the unified "Stream Air Traffic" step. - - This step replaces the provider-specific steps: - - "Generate Simulated Air Traffic Data" (geojson) - - "Generate BlueSky Simulation Air Traffic Data" (bluesky) - - "Generate Bayesian Simulation Air Traffic Data" (bayesian) - - "Fetch OpenSky Data" (opensky) - - All providers can now be used with a single consistent interface. - -steps: - # Example 1: GeoJSON provider with data generation only (no delivery) - - step: Stream Air Traffic - id: geojson_only - arguments: - provider: geojson - duration: 10 - target: none # Don't send anywhere, just return data - config_path: config/bern/trajectory_f1.json - number_of_aircraft: 2 - - # Example 2: GeoJSON with delivery to Flight Blender - - step: Stream Air Traffic - id: geojson_stream - arguments: - provider: geojson - duration: 30 - target: flight_blender - config_path: config/bern/trajectory_f1.json - number_of_aircraft: 2 - session_ids: - - "550e8400-e29b-41d4-a716-446655440001" - - # Example 3: BlueSky simulator (requires bluesky-simulator package) - # - step: Stream Air Traffic - # id: bluesky_stream - # arguments: - # provider: bluesky - # duration: 30 - # target: flight_blender - # config_path: config/bern/blue_sky_sim_bern.scn - - # Example 4: Live OpenSky data for Switzerland region - # - step: Stream Air Traffic - # id: opensky_live - # arguments: - # provider: opensky - # duration: 30 - # target: flight_blender - # viewport: [45.8389, 47.8229, 5.9962, 10.5226] diff --git a/src/openutm_verification/core/clients/air_traffic/air_traffic_client.py b/src/openutm_verification/core/clients/air_traffic/air_traffic_client.py index 11718678..97db0b36 100644 --- a/src/openutm_verification/core/clients/air_traffic/air_traffic_client.py +++ b/src/openutm_verification/core/clients/air_traffic/air_traffic_client.py @@ -13,7 +13,7 @@ from openutm_verification.core.clients.flight_blender.base_client import ( BaseBlenderAPIClient, ) -from openutm_verification.core.execution.scenario_runner import scenario_step +from openutm_verification.core.execution.scenario_runner import internal_step from openutm_verification.core.flight_phase import FlightPhase from openutm_verification.simulator.geo_json_telemetry import ( GeoJSONAirtrafficSimulator, @@ -34,7 +34,7 @@ def __init__(self, settings: AirTrafficSettings): # but we inherit from it. Ideally, we should refactor to composition over inheritance. BaseBlenderAPIClient.__init__(self, base_url="", credentials={}) - @scenario_step("Fetch Session IDs", phase=FlightPhase.PRE_FLIGHT) + @internal_step("Fetch Session IDs", phase=FlightPhase.PRE_FLIGHT) async def get_configured_session_ids( self, ) -> list[UUID]: @@ -55,7 +55,7 @@ async def get_configured_session_ids( raise return session_ids - @scenario_step("Generate Simulated Air Traffic Data", phase=FlightPhase.PRE_FLIGHT) + @internal_step("Generate Simulated Air Traffic Data", phase=FlightPhase.PRE_FLIGHT) async def generate_simulated_air_traffic_data( self, config_path: str | None = None, @@ -106,7 +106,7 @@ async def generate_simulated_air_traffic_data( logger.error(f"Failed to generate telemetry states from {config_path}: {exc}") raise - @scenario_step("Generate Simulated Air Traffic Data with Latency", phase=FlightPhase.PRE_FLIGHT) + @internal_step("Generate Simulated Air Traffic Data with Latency", phase=FlightPhase.PRE_FLIGHT) async def generate_simulated_air_traffic_data_with_latency( self, config_path: str | None = None, diff --git a/src/openutm_verification/core/clients/air_traffic/base_client.py b/src/openutm_verification/core/clients/air_traffic/base_client.py index 62679126..cf7d110e 100644 --- a/src/openutm_verification/core/clients/air_traffic/base_client.py +++ b/src/openutm_verification/core/clients/air_traffic/base_client.py @@ -10,13 +10,7 @@ if TYPE_CHECKING: from openutm_verification.core.execution.config_models import ( - AirTrafficSimulatorSettings as AirTrafficSimConfig, - ) - from openutm_verification.core.execution.config_models import ( - BayesianAirTrafficSimulatorSettings as BayesianSimConfig, - ) - from openutm_verification.core.execution.config_models import ( - BlueSkyAirTrafficSimulatorSettings as BlueSkySimConfig, + AirTrafficSimulatorSettings as SimConfig, ) @@ -35,7 +29,7 @@ class AirTrafficSettings(BaseModel): session_ids: list[str] = [] @classmethod - def from_config(cls, sim_config: "AirTrafficSimConfig", trajectory_path: str | None = None) -> "AirTrafficSettings": + def from_config(cls, sim_config: "SimConfig", trajectory_path: str | None = None) -> "AirTrafficSettings": """Create settings from config.""" return cls( simulation_config_path=trajectory_path or "", @@ -58,7 +52,7 @@ class BlueSkyAirTrafficSettings(BaseModel): session_ids: list[str] = [] @classmethod - def from_config(cls, sim_config: "BlueSkySimConfig", simulation_path: str | None = None) -> "BlueSkyAirTrafficSettings": + def from_config(cls, sim_config: "SimConfig", simulation_path: str | None = None) -> "BlueSkyAirTrafficSettings": """Create settings from config.""" return cls( simulation_config_path=simulation_path or "", @@ -82,7 +76,7 @@ class BayesianAirTrafficSettings(BaseModel): session_ids: list[str] = [] @classmethod - def from_config(cls, sim_config: "BayesianSimConfig", simulation_path: str | None = None) -> "BayesianAirTrafficSettings": + def from_config(cls, sim_config: "SimConfig", simulation_path: str | None = None) -> "BayesianAirTrafficSettings": """Create settings from config.""" return cls( simulation_config_path=simulation_path or "", diff --git a/src/openutm_verification/core/clients/air_traffic/bayesian_air_traffic_client.py b/src/openutm_verification/core/clients/air_traffic/bayesian_air_traffic_client.py index 749014ab..3e544f33 100644 --- a/src/openutm_verification/core/clients/air_traffic/bayesian_air_traffic_client.py +++ b/src/openutm_verification/core/clients/air_traffic/bayesian_air_traffic_client.py @@ -19,7 +19,7 @@ from openutm_verification.core.clients.flight_blender.base_client import ( BaseBlenderAPIClient, ) -from openutm_verification.core.execution.scenario_runner import scenario_step +from openutm_verification.core.execution.scenario_runner import internal_step from openutm_verification.core.flight_phase import FlightPhase from openutm_verification.simulator.models.flight_data_types import FlightObservationSchema @@ -75,7 +75,7 @@ def __init__(self, settings: BayesianAirTrafficSettings): # but we inherit from it. Ideally, we should refactor to composition over inheritance. BaseBlenderAPIClient.__init__(self, base_url="", credentials={}) - @scenario_step("Fetch Session IDs for Bayesian Simulation", phase=FlightPhase.PRE_FLIGHT) + @internal_step("Fetch Session IDs for Bayesian Simulation", phase=FlightPhase.PRE_FLIGHT) async def get_configured_bayesian_session_ids( self, ) -> list[UUID]: @@ -98,7 +98,7 @@ async def get_configured_bayesian_session_ids( raise return session_ids - @scenario_step("Generate Bayesian Simulation Air Traffic Data", phase=FlightPhase.PRE_FLIGHT) + @internal_step("Generate Bayesian Simulation Air Traffic Data", phase=FlightPhase.PRE_FLIGHT) async def generate_bayesian_sim_air_traffic_data( self, config_path: str | None = None, @@ -275,7 +275,7 @@ def _convert_track_to_observations( return observations - @scenario_step("Generate Bayesian Simulation Air Traffic Data with latency issues", phase=FlightPhase.PRE_FLIGHT) + @internal_step("Generate Bayesian Simulation Air Traffic Data with latency issues", phase=FlightPhase.PRE_FLIGHT) async def generate_bayesian_sim_air_traffic_data_with_sensor_latency_issues( self, config_path: str | None = None, diff --git a/src/openutm_verification/core/clients/air_traffic/blue_sky_client.py b/src/openutm_verification/core/clients/air_traffic/blue_sky_client.py index f97820e1..9b552a82 100644 --- a/src/openutm_verification/core/clients/air_traffic/blue_sky_client.py +++ b/src/openutm_verification/core/clients/air_traffic/blue_sky_client.py @@ -21,7 +21,7 @@ from openutm_verification.core.clients.flight_blender.base_client import ( BaseBlenderAPIClient, ) -from openutm_verification.core.execution.scenario_runner import scenario_step +from openutm_verification.core.execution.scenario_runner import internal_step from openutm_verification.core.flight_phase import FlightPhase from openutm_verification.simulator.models.flight_data_types import ( FlightObservationSchema, @@ -38,7 +38,7 @@ def __init__(self, settings: BlueSkyAirTrafficSettings): # but we inherit from it. Ideally, we should refactor to composition over inheritance. BaseBlenderAPIClient.__init__(self, base_url="", credentials={}) - @scenario_step("Generate BlueSky Simulation Air Traffic Data", phase=FlightPhase.PRE_FLIGHT) + @internal_step("Generate BlueSky Simulation Air Traffic Data", phase=FlightPhase.PRE_FLIGHT) async def generate_bluesky_sim_air_traffic_data( self, config_path: str | None = None, @@ -154,7 +154,7 @@ async def generate_bluesky_sim_air_traffic_data( all_obs.extend(results_by_acid[acid]) return all_obs - @scenario_step("Generate BlueSky Simulation Air Traffic Data with latency issues", phase=FlightPhase.PRE_FLIGHT) + @internal_step("Generate BlueSky Simulation Air Traffic Data with latency issues", phase=FlightPhase.PRE_FLIGHT) async def generate_bluesky_sim_air_traffic_data_with_sensor_latency_issues( self, config_path: str | None = None, diff --git a/src/openutm_verification/core/clients/opensky/opensky_client.py b/src/openutm_verification/core/clients/opensky/opensky_client.py index 728f7a75..a58ed6f7 100644 --- a/src/openutm_verification/core/clients/opensky/opensky_client.py +++ b/src/openutm_verification/core/clients/opensky/opensky_client.py @@ -5,7 +5,7 @@ BaseOpenSkyAPIClient, OpenSkySettings, ) -from openutm_verification.core.execution.scenario_runner import scenario_step +from openutm_verification.core.execution.scenario_runner import internal_step from openutm_verification.core.flight_phase import FlightPhase from openutm_verification.simulator.models.flight_data_types import ( FlightObservationSchema, @@ -103,7 +103,7 @@ async def fetch_and_process_data(self) -> list[FlightObservationSchema] | None: return self.process_flight_data(flight_df) - @scenario_step("Fetch OpenSky Data", phase=FlightPhase.CRUISE) + @internal_step("Fetch OpenSky Data", phase=FlightPhase.CRUISE) async def fetch_data(self) -> list[FlightObservationSchema] | None: """Fetch and process live flight data from OpenSky Network. diff --git a/src/openutm_verification/core/execution/config_models.py b/src/openutm_verification/core/execution/config_models.py index d0fd0ad2..044c2004 100644 --- a/src/openutm_verification/core/execution/config_models.py +++ b/src/openutm_verification/core/execution/config_models.py @@ -48,22 +48,6 @@ def validate_duration(cls, v: int | str) -> int: return int(parse_duration(v)) -class BlueSkyAirTrafficSimulatorSettings(StrictBaseModel): - number_of_aircraft: int - simulation_duration: int - single_or_multiple_sensors: Literal["single", "multiple"] = "single" - sensor_ids: list[str] = Field(default_factory=list) - session_ids: list[str] = Field(default_factory=list) - - -class BayesianAirTrafficSimulatorSettings(StrictBaseModel): - number_of_aircraft: int - simulation_duration: int - single_or_multiple_sensors: Literal["single", "multiple"] = "single" - sensor_ids: list[str] = Field(default_factory=list) - session_ids: list[str] = Field(default_factory=list) - - class AMQPConfig(StrictBaseModel): """AMQP/RabbitMQ connection configuration.""" @@ -197,9 +181,7 @@ class AppConfig(StrictBaseModel): run_id: str = "daily-conformance-check" flight_blender: FlightBlenderConfig opensky: OpenSkyConfig - air_traffic_simulator_settings: AirTrafficSimulatorSettings - blue_sky_air_traffic_simulator_settings: BlueSkyAirTrafficSimulatorSettings - bayesian_air_traffic_simulator_settings: BayesianAirTrafficSimulatorSettings + air_traffic_simulator_settings: AirTrafficSimulatorSettings | None = None amqp: AMQPConfig | None = None # Optional AMQP/RabbitMQ configuration data_files: DataFiles suites: dict[str, SuiteConfig] = Field(default_factory=dict) diff --git a/src/openutm_verification/core/execution/scenario_runner.py b/src/openutm_verification/core/execution/scenario_runner.py index f14ad1ea..7cc69f4a 100644 --- a/src/openutm_verification/core/execution/scenario_runner.py +++ b/src/openutm_verification/core/execution/scenario_runner.py @@ -208,12 +208,13 @@ def air_traffic_data(self) -> list[FlightObservationSchema]: class ScenarioStepDescriptor: - def __init__(self, func: Callable[..., Awaitable[Any]], step_name: str, phase: FlightPhase | None = None): + def __init__(self, func: Callable[..., Awaitable[Any]], step_name: str, phase: FlightPhase | None = None, *, register: bool = True): self.func = func self.step_name = step_name self.phase = phase + self.register = register self.wrapper = self._create_wrapper(func, step_name, phase) - self.param_model = self._create_param_model(func, step_name) + self.param_model = self._create_param_model(func, step_name) if register else None def _create_param_model(self, func: Callable[..., Any], step_name: str) -> type[BaseModel]: sig = inspect.signature(func) @@ -331,16 +332,17 @@ def log_filter(record): return async_wrapper def __set_name__(self, owner: type, name: str): - # Register using the human-readable step name - registry_key = self.step_name - if registry_key in STEP_REGISTRY: - logger.warning(f"Overwriting step registry for '{registry_key}'. Ensure step names are unique.") - - STEP_REGISTRY[registry_key] = StepRegistryEntry( - client_class=owner, - method_name=name, - param_model=self.param_model, - ) + if self.register: + # Register using the human-readable step name + registry_key = self.step_name + if registry_key in STEP_REGISTRY: + logger.warning(f"Overwriting step registry for '{registry_key}'. Ensure step names are unique.") + + STEP_REGISTRY[registry_key] = StepRegistryEntry( + client_class=owner, + method_name=name, + param_model=self.param_model, + ) setattr(owner, name, self.wrapper) def __call__(self, *args: Any, **kwargs: Any): @@ -352,3 +354,24 @@ def decorator(func: Callable[..., Awaitable[Any]]) -> Any: return ScenarioStepDescriptor(func, step_name, phase) return decorator + + +def internal_step(step_name: str, phase: FlightPhase | None = None) -> Callable[[Callable[..., Awaitable[Any]]], Any]: + """Decorator for helper methods that should NOT be exposed as YAML operations. + + Behaves like @scenario_step (StepResult wrapping, timing, logging, error + handling, captured logs) but skips registration in STEP_REGISTRY. As a result + the method: + + - is not callable from YAML scenarios via ``step: `` + - is not surfaced in the web-editor operations toolbox + - is not enumerated by ``/operations`` or any STEP_REGISTRY-backed introspection + + It will still appear in run telemetry / reports when invoked internally, + typically with ``id=None``. + """ + + def decorator(func: Callable[..., Awaitable[Any]]) -> Any: + return ScenarioStepDescriptor(func, step_name, phase, register=False) + + return decorator diff --git a/src/openutm_verification/core/steps/air_traffic_step.py b/src/openutm_verification/core/steps/air_traffic_step.py index f427c77d..0ee28c3b 100644 --- a/src/openutm_verification/core/steps/air_traffic_step.py +++ b/src/openutm_verification/core/steps/air_traffic_step.py @@ -95,8 +95,8 @@ def _apply_config_defaults( return (resolved_duration, config_path, number_of_aircraft, sensor_ids, session_ids) try: - if provider == ProviderType.GEOJSON: - sim = app_config.air_traffic_simulator_settings + sim = app_config.air_traffic_simulator_settings + if sim and provider != ProviderType.OPENSKY: if duration is None: duration = sim.simulation_duration if number_of_aircraft is None: @@ -105,35 +105,13 @@ def _apply_config_defaults( sensor_ids = sim.sensor_ids if session_ids is None and sim.session_ids: session_ids = sim.session_ids - if config_path is None: - config_path = _get_data_file_path("trajectory") - elif provider == ProviderType.BLUESKY: - sim = app_config.blue_sky_air_traffic_simulator_settings - if duration is None: - duration = sim.simulation_duration - if number_of_aircraft is None: - number_of_aircraft = sim.number_of_aircraft - if sensor_ids is None and sim.sensor_ids: - sensor_ids = sim.sensor_ids - if session_ids is None and sim.session_ids: - session_ids = sim.session_ids - if config_path is None: + # Resolve config_path from data_files when not explicitly provided + if config_path is None: + if provider == ProviderType.GEOJSON: + config_path = _get_data_file_path("trajectory") + elif provider == ProviderType.BLUESKY: config_path = _get_data_file_path("simulation") - - elif provider == ProviderType.BAYESIAN: - sim = app_config.bayesian_air_traffic_simulator_settings - if duration is None: - duration = sim.simulation_duration - if number_of_aircraft is None: - number_of_aircraft = sim.number_of_aircraft - if sensor_ids is None and sim.sensor_ids: - sensor_ids = sim.sensor_ids - if session_ids is None and sim.session_ids: - session_ids = sim.session_ids - - elif provider == ProviderType.OPENSKY: - pass # OpenSky reads its own config in the provider except Exception: logger.debug("Could not read application config for defaults, using step arguments only.") diff --git a/tests/test_client_settings.py b/tests/test_client_settings.py index 15313d97..cebbff07 100644 --- a/tests/test_client_settings.py +++ b/tests/test_client_settings.py @@ -16,7 +16,6 @@ AirTrafficSimulatorSettings, AMQPConfig, AuthConfig, - BlueSkyAirTrafficSimulatorSettings, OpenSkyConfig, ) @@ -168,7 +167,7 @@ class TestBlueSkyAirTrafficSettingsFromConfig: def test_from_config_all_fields(self): """All config fields are correctly mapped.""" - sim_config = BlueSkyAirTrafficSimulatorSettings( + sim_config = AirTrafficSimulatorSettings( number_of_aircraft=10, simulation_duration=120, single_or_multiple_sensors="single", @@ -190,7 +189,7 @@ def test_from_config_all_fields(self): def test_from_config_no_simulation(self): """Settings work without simulation path.""" - sim_config = BlueSkyAirTrafficSimulatorSettings( + sim_config = AirTrafficSimulatorSettings( number_of_aircraft=3, simulation_duration=30, ) @@ -202,7 +201,7 @@ def test_from_config_no_simulation(self): def test_from_config_defaults(self): """Default values work correctly.""" - sim_config = BlueSkyAirTrafficSimulatorSettings( + sim_config = AirTrafficSimulatorSettings( number_of_aircraft=2, simulation_duration=30, ) diff --git a/tests/test_reporting_output.py b/tests/test_reporting_output.py index ccd3b3bc..14a35908 100644 --- a/tests/test_reporting_output.py +++ b/tests/test_reporting_output.py @@ -6,8 +6,6 @@ AirTrafficSimulatorSettings, AppConfig, AuthConfig, - BayesianAirTrafficSimulatorSettings, - BlueSkyAirTrafficSimulatorSettings, DataFiles, DeploymentDetails, FlightBlenderConfig, @@ -28,12 +26,6 @@ def _make_app_config(tmp_path: Path) -> AppConfig: air_traffic_simulator_settings=AirTrafficSimulatorSettings( number_of_aircraft=1, simulation_duration=1, single_or_multiple_sensors="single", sensor_ids=[] ), - blue_sky_air_traffic_simulator_settings=BlueSkyAirTrafficSimulatorSettings( - number_of_aircraft=1, simulation_duration=1, single_or_multiple_sensors="single", sensor_ids=[] - ), - bayesian_air_traffic_simulator_settings=BayesianAirTrafficSimulatorSettings( - number_of_aircraft=1, simulation_duration=1, single_or_multiple_sensors="single", sensor_ids=[] - ), data_files=DataFiles(), suites={}, reporting=ReportingConfig(output_dir=str(tmp_path), formats=["json", "html"], deployment_details=DeploymentDetails()), diff --git a/tests/test_suite_scenario_merge.py b/tests/test_suite_scenario_merge.py index 47d34a7e..206deca3 100644 --- a/tests/test_suite_scenario_merge.py +++ b/tests/test_suite_scenario_merge.py @@ -134,14 +134,6 @@ def test_config_loading_merges_defaults(self, tmp_path): "number_of_aircraft": 3, "simulation_duration": 10, }, - "blue_sky_air_traffic_simulator_settings": { - "number_of_aircraft": 3, - "simulation_duration": 30, - }, - "bayesian_air_traffic_simulator_settings": { - "number_of_aircraft": 3, - "simulation_duration": 30, - }, "data_files": { "trajectory": "default_traj.json", "flight_declaration": "default_fd.json", @@ -210,14 +202,6 @@ def test_daa_scenario_config_example(self, tmp_path): "number_of_aircraft": 3, "simulation_duration": 10, }, - "blue_sky_air_traffic_simulator_settings": { - "number_of_aircraft": 3, - "simulation_duration": 30, - }, - "bayesian_air_traffic_simulator_settings": { - "number_of_aircraft": 3, - "simulation_duration": 30, - }, "data_files": { "trajectory": "trajectory_f1.json", # DEFAULT "flight_declaration": "fd.json", diff --git a/web-editor/README_GROUPS.md b/web-editor/README_GROUPS.md index e29836da..5d4f43d1 100644 --- a/web-editor/README_GROUPS.md +++ b/web-editor/README_GROUPS.md @@ -19,7 +19,7 @@ Located in the **Scenario Settings** right sidebar, the Groups Manager allows yo Each step in a group has: - **Step ID**: Unique identifier within the group (e.g., `fetch`, `submit`) -- **Step Name**: The operation to execute (e.g., "Fetch OpenSky Data") +- **Step Name**: The operation to execute (e.g., "Stream Air Traffic") - **Arguments**: Parameters for the operation, supporting internal group references ### 3. Internal Group References @@ -54,19 +54,17 @@ In the main scenario flow: ## Example Workflow 1. **Create a group** in the Groups Manager: - - Name: `fetch_and_submit` - - Add step 1: "Fetch OpenSky Data" (id: `fetch`) - - Add step 2: "Submit Air Traffic" (id: `submit`) - - Set submit's observations argument to: `${{ group.fetch.result }}` + - Name: `stream_traffic` + - Add step 1: "Stream Air Traffic" with `provider: opensky`, `target: flight_blender` (id: `stream`) 2. **Use the group** in the main flow: - - Add a step that references `fetch_and_submit` + - Add a step that references `stream_traffic` - Set it to loop with count 5 - The group will execute all its steps 5 times 3. **Reference group results** in subsequent steps: - - After the group, add "Process Results" - - Reference the group's submit step: `${{ group.submit.result }}` + - After the group, add a follow-up step + - Reference the group's stream step: `${{ group.stream.result }}` - This will get the last iteration's result ## Exporting to YAML @@ -76,17 +74,16 @@ When you save a scenario with groups, the YAML will include: ```yaml name: my_scenario groups: - fetch_and_submit: - description: Fetches and submits air traffic data + stream_traffic: + description: Streams air traffic from OpenSky steps: - - id: fetch - step: Fetch OpenSky Data - - id: submit - step: Submit Air Traffic + - id: stream + step: Stream Air Traffic arguments: - observations: ${{ group.fetch.result }} + provider: opensky + duration: 30 steps: - - step: fetch_and_submit + - step: stream_traffic loop: count: 5 ``` diff --git a/web-editor/async_implementation_guide.md b/web-editor/async_implementation_guide.md index e9d2eadd..1ca05af0 100644 --- a/web-editor/async_implementation_guide.md +++ b/web-editor/async_implementation_guide.md @@ -52,14 +52,15 @@ To create a scenario with async tasks (e.g., `sdsp_track`): ### Example YAML (`scenarios/sdsp_track.yaml`) ```yaml - - step: Submit Simulated Air Traffic + - step: Stream Air Traffic arguments: - observations: ${{ steps.Generate Simulated Air Traffic Data.result }} + provider: geojson + duration: 30 background: true # ... other steps running in parallel ... - step: Other step that needs to wait for BG task to finish. needs: - - Submit Simulated Air Traffic + - Stream Air Traffic ``` diff --git a/web-editor/src/components/ScenarioEditor.tsx b/web-editor/src/components/ScenarioEditor.tsx index cae55b9c..841b259a 100644 --- a/web-editor/src/components/ScenarioEditor.tsx +++ b/web-editor/src/components/ScenarioEditor.tsx @@ -68,14 +68,8 @@ const ScenarioEditorContent = () => { air_traffic_simulator_settings: { number_of_aircraft: 3, simulation_duration: 10, - single_or_multiple_sensors: "multiple", + single_or_multiple_sensors: "single", sensor_ids: ["a0b7d47e5eac45dc8cbaf47e6fe0e558"] - }, - blue_sky_air_traffic_simulator_settings: { - number_of_aircraft: 3, - simulation_duration: 30, - single_or_multiple_sensors: "multiple", - sensor_ids: ["562e6297036a4adebb4848afcd1ede90"] } }); diff --git a/web-editor/src/components/ScenarioEditor/ConfigEditor.tsx b/web-editor/src/components/ScenarioEditor/ConfigEditor.tsx index 6187bd17..625e8ce0 100644 --- a/web-editor/src/components/ScenarioEditor/ConfigEditor.tsx +++ b/web-editor/src/components/ScenarioEditor/ConfigEditor.tsx @@ -6,9 +6,7 @@ import type { ScenarioConfig } from '../../types/scenario'; const sectionTooltips: Record = { flight_blender: 'Configure connection to Flight Blender, the UTM integration server. Set the URL, authentication method, and OAuth scopes.', data_files: 'Specify paths to data files used in the scenario: trajectories, flight declarations, operational intents, and geo-fences.', - air_traffic: 'Settings for the default air traffic simulator including number of aircraft, simulation duration, and sensor configuration.', - blue_sky_air_traffic: 'Settings for the BlueSky air traffic simulator, an alternative simulator with its own aircraft count, duration, and sensor IDs.', - bayesian_air_traffic: 'Settings for the Bayesian air traffic simulator, including number of aircraft, simulation duration, and sensor configuration.', + air_traffic: 'Default air traffic simulator settings: number of aircraft, simulation duration, and sensor configuration. Per-step overrides go in scenario YAML arguments.', }; const Tooltip: React.FC<{ text: string }> = ({ text }) => ( @@ -81,21 +79,11 @@ export const ConfigEditor: React.FC = ({ config, onUpdateConf }); }; - const updateAirTrafficSimulator = (field: string, value: string | number | string[]) => { + const updateAirTrafficSettings = (field: string, value: string | number | string[]) => { onUpdateConfig({ ...config, air_traffic_simulator_settings: { - ...config.air_traffic_simulator_settings, - [field]: value - } - }); - }; - - const updateBlueSkyAirTrafficSimulator = (field: string, value: string | number | string[]) => { - onUpdateConfig({ - ...config, - blue_sky_air_traffic_simulator_settings: { - ...(config.blue_sky_air_traffic_simulator_settings || {}), + ...(config.air_traffic_simulator_settings || {}), [field]: value } }); @@ -103,27 +91,7 @@ export const ConfigEditor: React.FC = ({ config, onUpdateConf const updateSensorIds = (value: string) => { const ids = value.split(',').map(id => id.trim()).filter(id => id.length > 0); - updateAirTrafficSimulator('sensor_ids', ids); - }; - - const updateBlueSkySensorIds = (value: string) => { - const ids = value.split(',').map(id => id.trim()).filter(id => id.length > 0); - updateBlueSkyAirTrafficSimulator('sensor_ids', ids); - }; - - const updateBayesianAirTrafficSimulator = (field: string, value: string | number | string[]) => { - onUpdateConfig({ - ...config, - bayesian_air_traffic_simulator_settings: { - ...(config.bayesian_air_traffic_simulator_settings || {}), - [field]: value - } - }); - }; - - const updateBayesianSensorIds = (value: string) => { - const ids = value.split(',').map(id => id.trim()).filter(id => id.length > 0); - updateBayesianAirTrafficSimulator('sensor_ids', ids); + updateAirTrafficSettings('sensor_ids', ids); }; return ( @@ -360,7 +328,7 @@ export const ConfigEditor: React.FC = ({ config, onUpdateConf )} - {/* Air Traffic Simulator Section */} + {/* Air Traffic Simulator Settings */}
{showHelp && }
- - )} - - - {/* BlueSky Air Traffic Simulator Section */} -
- {showHelp && } - - - {expandedSections.has('blue_sky_air_traffic') && ( -
-
- - updateBlueSkyAirTrafficSimulator('number_of_aircraft', parseInt(e.target.value))} - min="1" - max="100" - /> -
- -
- - updateBlueSkyAirTrafficSimulator('simulation_duration', parseInt(e.target.value))} - min="1" - max="3600" - /> -
- -
- - -
- -
- - updateBlueSkySensorIds(e.target.value)} - placeholder="562e6297036a4adebb4848afcd1ede90" - /> -
-
- )} -
- - {/* Bayesian Air Traffic Simulator Section */} -
- {showHelp && } - - - {expandedSections.has('bayesian_air_traffic') && ( -
-
- - updateBayesianAirTrafficSimulator('number_of_aircraft', parseInt(e.target.value))} - min="1" - max="100" - /> -
- -
- - updateBayesianAirTrafficSimulator('simulation_duration', parseInt(e.target.value))} - min="1" - max="3600" - /> -
- -
- - -
- -
- - updateBayesianSensorIds(e.target.value)} - placeholder="562e6297036a4adebb4848afcd1ede90" + placeholder="sensor-id-uuid" />
diff --git a/web-editor/src/hooks/__tests__/useScenarioFile.test.ts b/web-editor/src/hooks/__tests__/useScenarioFile.test.ts index f2d70f19..6637048d 100644 --- a/web-editor/src/hooks/__tests__/useScenarioFile.test.ts +++ b/web-editor/src/hooks/__tests__/useScenarioFile.test.ts @@ -16,8 +16,7 @@ describe('useScenarioFile', () => { auth: { type: "none", audience: "test", scopes: [] } }, data_files: {}, - air_traffic_simulator_settings: {}, - blue_sky_air_traffic_simulator_settings: {} + air_traffic_simulator_settings: {} }; beforeEach(() => { diff --git a/web-editor/src/hooks/__tests__/useScenarioRunner.test.ts b/web-editor/src/hooks/__tests__/useScenarioRunner.test.ts index eacf8347..13cb7d2f 100644 --- a/web-editor/src/hooks/__tests__/useScenarioRunner.test.ts +++ b/web-editor/src/hooks/__tests__/useScenarioRunner.test.ts @@ -158,8 +158,7 @@ describe('useScenarioRunner', () => { auth: { type: "none", audience: "test", scopes: [] } }, data_files: {}, - air_traffic_simulator_settings: {}, - blue_sky_air_traffic_simulator_settings: {} + air_traffic_simulator_settings: {} }; await act(async () => { diff --git a/web-editor/src/types/scenario.ts b/web-editor/src/types/scenario.ts index 5f7b83f8..ffcc7fbc 100644 --- a/web-editor/src/types/scenario.ts +++ b/web-editor/src/types/scenario.ts @@ -113,24 +113,8 @@ export interface AirTrafficSimulatorSettings { sensor_ids?: string[]; } -export interface BlueSkyAirTrafficSimulatorSettings { - number_of_aircraft?: number; - simulation_duration?: number; - single_or_multiple_sensors?: string; - sensor_ids?: string[]; -} - -export interface BayesianAirTrafficSimulatorSettings { - number_of_aircraft?: number; - simulation_duration?: number; - single_or_multiple_sensors?: string; - sensor_ids?: string[]; -} - export interface ScenarioConfig { flight_blender: FlightBlenderConfig; data_files: DataFilesConfig; - air_traffic_simulator_settings: AirTrafficSimulatorSettings; - blue_sky_air_traffic_simulator_settings?: BlueSkyAirTrafficSimulatorSettings; - bayesian_air_traffic_simulator_settings?: BayesianAirTrafficSimulatorSettings; + air_traffic_simulator_settings?: AirTrafficSimulatorSettings; }