117 lines
3.6 KiB
Python
117 lines
3.6 KiB
Python
"""Finite read scopes available to operational investigations."""
|
|
from datetime import timedelta
|
|
from typing import Literal
|
|
|
|
from pydantic import Field, JsonValue, model_validator
|
|
|
|
from app.domain.schemas.shared import NonEmptyStr, StrictModel
|
|
from app.domain.schemas.workbench_runtime import WorkbenchDateTime
|
|
|
|
|
|
TaskDataSource = Literal[
|
|
"workbench_runtime", "scada_devices", "scada_readings", "peripheral_points",
|
|
"peripheral_work_orders", "peripheral_observations", "peripheral_test_data",
|
|
|
|
]
|
|
|
|
|
|
McpSnapshotSource = Literal[
|
|
'get_mutation_status',
|
|
'query_nearby_elements',
|
|
'list_regions',
|
|
'get_region',
|
|
'get_region_nodes',
|
|
'get_node_connections',
|
|
'query_network_extent',
|
|
'list_major_nodes',
|
|
'get_model_configuration',
|
|
'list_model_configurations',
|
|
'search_project_places',
|
|
'reverse_geocode',
|
|
'get_project_info',
|
|
'export_project_model',
|
|
'get_sensor_placement_candidate',
|
|
'export_sensor_placement',
|
|
'list_task_schedules',
|
|
'get_task_run_history',
|
|
'calculate_demand_allocation',
|
|
'run_burst_simulation',
|
|
'edit_model_element',
|
|
'edit_model_configuration',
|
|
'clean_scada_readings',
|
|
'write_scada_readings',
|
|
'delete_scada_readings',
|
|
'save_simulation_scenario',
|
|
'create_sensor_placement',
|
|
'update_sensor_placement',
|
|
'create_operational_task',
|
|
'create_task_schedule',
|
|
'update_task_schedule',
|
|
'retry_operational_task',
|
|
'cancel_task_run',
|
|
'run_simulation',
|
|
'run_contaminant_simulation',
|
|
'analyze_valve_closure',
|
|
'analyze_flushing',
|
|
'analyze_pressure_regulation',
|
|
'optimize_sensor_placement',
|
|
'run_burst_detection',
|
|
'run_burst_location',
|
|
'analyze_valve_isolation',
|
|
'run_leakage_identification',
|
|
'get_element_properties',
|
|
'list_elements',
|
|
'get_network_options',
|
|
'get_network_schema',
|
|
'list_scada_devices',
|
|
'get_scada_device',
|
|
'query_scada_readings',
|
|
'query_model_baseline_series',
|
|
'get_model_baseline_at_time',
|
|
'query_model_baseline_field',
|
|
'query_analysis_series',
|
|
'query_analysis_values',
|
|
'list_analysis_runs',
|
|
'get_analysis_run',
|
|
'list_analysis_results',
|
|
'list_sensor_placement_runs',
|
|
'get_sensor_placement_run',
|
|
'query_element_history',
|
|
'get_element_gis_properties',
|
|
'get_source_gis_properties',
|
|
'get_workbench_runtime',
|
|
'get_task_context',
|
|
'query_peripheral_data',
|
|
'get_task_evidence',
|
|
]
|
|
TaskSnapshotSource = TaskDataSource | McpSnapshotSource
|
|
|
|
class TaskDataQuery(StrictModel):
|
|
source: TaskDataSource
|
|
device_ids: list[NonEmptyStr] = Field(default_factory=list, max_length=20)
|
|
point_id: NonEmptyStr | None = None
|
|
start: WorkbenchDateTime | None = None
|
|
end: WorkbenchDateTime | None = None
|
|
|
|
@model_validator(mode="after")
|
|
def validate_window(self):
|
|
if self.source in {"scada_readings", "peripheral_observations"}:
|
|
if self.start is None or self.end is None:
|
|
raise ValueError("time series queries require start and end")
|
|
if not timedelta(0) < self.end - self.start <= timedelta(days=1):
|
|
raise ValueError("query window must be positive and at most 24 hours")
|
|
if self.source == "scada_readings" and not self.device_ids:
|
|
raise ValueError("SCADA queries require device_ids")
|
|
if self.source == "peripheral_observations" and not self.point_id:
|
|
raise ValueError("peripheral queries require point_id")
|
|
return self
|
|
|
|
|
|
class TaskDataSnapshot(StrictModel):
|
|
source: TaskSnapshotSource
|
|
source_ref: NonEmptyStr
|
|
queried_at: WorkbenchDateTime
|
|
origin: Literal["system_data", "synthetic"]
|
|
data: JsonValue
|
|
truncated: bool
|