landvex: Fixar och tester klara för alla komponenter
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Weather Intelligence Pipeline v2
|
||||
Road Binding → Risk Profiles → Event Generation → Mission Engine
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, '/home/bernt/.openclaw/workspace/life-weather')
|
||||
|
||||
from providers.weather_provider import WeatherProvider
|
||||
from storage.observation_store import ObservationStore
|
||||
from engine.risk_engine import RiskEngine
|
||||
|
||||
LOG_DIR = Path("/home/bernt/.openclaw/workspace/life-weather/logs")
|
||||
LOG_DIR.mkdir(exist_ok=True)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler(LOG_DIR / "pipeline_v2.log"),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class RoadBinding:
|
||||
"""Steg 1: Koppla väg till väderobservation"""
|
||||
|
||||
def __init__(self, store):
|
||||
self.store = store
|
||||
|
||||
def bind_road(self, road_id, weather_data):
|
||||
"""Koppla väg till väderobservation"""
|
||||
logger.info(f"Road {road_id}: Binding to weather observation")
|
||||
|
||||
observations = []
|
||||
|
||||
if weather_data.get("temperature"):
|
||||
obs_id = self.store.save_observation(
|
||||
road_id=road_id,
|
||||
observation_type="temperature",
|
||||
value=weather_data["temperature"]["value"],
|
||||
unit="celsius",
|
||||
timestamp=weather_data["timestamp"],
|
||||
raw_data=weather_data["temperature"]
|
||||
)
|
||||
if obs_id:
|
||||
observations.append(obs_id)
|
||||
|
||||
if weather_data.get("precipitation"):
|
||||
obs_id = self.store.save_observation(
|
||||
road_id=road_id,
|
||||
observation_type="precipitation",
|
||||
value=weather_data["precipitation"]["value"],
|
||||
unit="mm",
|
||||
timestamp=weather_data["timestamp"],
|
||||
raw_data=weather_data["precipitation"]
|
||||
)
|
||||
if obs_id:
|
||||
observations.append(obs_id)
|
||||
|
||||
if weather_data.get("wind"):
|
||||
obs_id = self.store.save_observation(
|
||||
road_id=road_id,
|
||||
observation_type="wind",
|
||||
value=weather_data["wind"]["value"],
|
||||
unit="m/s",
|
||||
timestamp=weather_data["timestamp"],
|
||||
raw_data=weather_data["wind"]
|
||||
)
|
||||
if obs_id:
|
||||
observations.append(obs_id)
|
||||
|
||||
return {
|
||||
"road_id": road_id,
|
||||
"observations": observations,
|
||||
"timestamp": weather_data["timestamp"]
|
||||
}
|
||||
|
||||
class RiskProfile:
|
||||
"""Steg 2: Skapa riskprofil för väg"""
|
||||
|
||||
def __init__(self, engine):
|
||||
self.engine = engine
|
||||
|
||||
def create_profile(self, road_id, road_type, weather_data):
|
||||
"""Skapa riskprofil"""
|
||||
logger.info(f"Road {road_id}: Creating risk profile")
|
||||
|
||||
temp = weather_data.get("temperature", {}).get("value") if weather_data.get("temperature") else None
|
||||
precip = weather_data.get("precipitation", {}).get("value") if weather_data.get("precipitation") else None
|
||||
wind = weather_data.get("wind", {}).get("value") if weather_data.get("wind") else None
|
||||
|
||||
risks = self.engine.calculate_risk(road_id, road_type, temp, precip, wind)
|
||||
|
||||
profile = {
|
||||
"road_id": road_id,
|
||||
"road_type": road_type,
|
||||
"timestamp": weather_data["timestamp"],
|
||||
"weather": {
|
||||
"temperature": temp,
|
||||
"precipitation": precip,
|
||||
"wind": wind
|
||||
},
|
||||
"risks": [
|
||||
{
|
||||
"type": r.risk_type,
|
||||
"severity": r.severity,
|
||||
"score": r.score,
|
||||
"description": r.description
|
||||
}
|
||||
for r in risks
|
||||
],
|
||||
"risk_count": len(risks),
|
||||
"max_severity": max([r.severity for r in risks], key=lambda x: {"low": 1, "medium": 2, "high": 3, "critical": 4}.get(x, 0)) if risks else "none"
|
||||
}
|
||||
|
||||
return profile
|
||||
|
||||
class EventGenerator:
|
||||
"""Steg 3: Generera händelser från risker"""
|
||||
|
||||
def __init__(self):
|
||||
self.events = []
|
||||
|
||||
def generate_events(self, risk_profile):
|
||||
"""Generera händelser från riskprofil"""
|
||||
road_id = risk_profile["road_id"]
|
||||
risks = risk_profile["risks"]
|
||||
|
||||
events = []
|
||||
for risk in risks:
|
||||
if risk["severity"] in ["high", "critical"]:
|
||||
event = {
|
||||
"event_id": f"evt_{road_id}_{risk['type']}_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
|
||||
"road_id": road_id,
|
||||
"event_type": risk["type"],
|
||||
"severity": risk["severity"],
|
||||
"description": risk["description"],
|
||||
"timestamp": risk_profile["timestamp"],
|
||||
"status": "active",
|
||||
"requires_verification": True
|
||||
}
|
||||
events.append(event)
|
||||
logger.info(f"Road {road_id}: Generated event {event['event_id']}")
|
||||
|
||||
return events
|
||||
|
||||
class MissionEngine:
|
||||
"""Steg 4: Generera missions från events"""
|
||||
|
||||
def __init__(self):
|
||||
self.missions = []
|
||||
|
||||
def generate_missions(self, events):
|
||||
"""Generera missions från events"""
|
||||
missions = []
|
||||
|
||||
for event in events:
|
||||
if event["requires_verification"]:
|
||||
mission = {
|
||||
"mission_id": f"mission_{event['event_id']}",
|
||||
"event_id": event["event_id"],
|
||||
"road_id": event["road_id"],
|
||||
"mission_type": "verification",
|
||||
"description": f"Verify {event['event_type']} on road {event['road_id']}",
|
||||
"priority": event["severity"],
|
||||
"status": "pending",
|
||||
"created_at": event["timestamp"],
|
||||
"target": "quixzoom" # Skicka till quiXzoom
|
||||
}
|
||||
missions.append(mission)
|
||||
logger.info(f"Road {event['road_id']}: Generated mission {mission['mission_id']}")
|
||||
|
||||
return missions
|
||||
|
||||
class WeatherPipelineV2:
|
||||
"""Huvudpipeline"""
|
||||
|
||||
def __init__(self):
|
||||
self.provider = WeatherProvider()
|
||||
self.store = ObservationStore()
|
||||
self.engine = RiskEngine()
|
||||
|
||||
self.road_binding = RoadBinding(self.store)
|
||||
self.risk_profile = RiskProfile(self.engine)
|
||||
self.event_generator = EventGenerator()
|
||||
self.mission_engine = MissionEngine()
|
||||
|
||||
def run_for_road(self, road_id, road_type="primary"):
|
||||
"""Kör pipeline för en väg"""
|
||||
logger.info(f"=== Pipeline for Road {road_id} ===")
|
||||
|
||||
# Steg 0: Hämta väderdata
|
||||
weather = self.provider.get_current_weather()
|
||||
if not weather.get("temperature"):
|
||||
logger.error("No weather data available")
|
||||
return None
|
||||
|
||||
# Steg 1: Road Binding
|
||||
binding = self.road_binding.bind_road(road_id, weather)
|
||||
|
||||
# Steg 2: Risk Profile
|
||||
profile = self.risk_profile.create_profile(road_id, road_type, weather)
|
||||
|
||||
# Steg 3: Event Generation
|
||||
events = self.event_generator.generate_events(profile)
|
||||
|
||||
# Steg 4: Mission Engine
|
||||
missions = self.mission_engine.generate_missions(events)
|
||||
|
||||
result = {
|
||||
"road_id": road_id,
|
||||
"timestamp": weather["timestamp"],
|
||||
"binding": binding,
|
||||
"profile": profile,
|
||||
"events": events,
|
||||
"missions": missions
|
||||
}
|
||||
|
||||
logger.info(f"Road {road_id}: Pipeline complete - {len(events)} events, {len(missions)} missions")
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
pipeline = WeatherPipelineV2()
|
||||
|
||||
# Kör för första vägen
|
||||
result = pipeline.run_for_road(road_id=1, road_type="primary")
|
||||
|
||||
if result:
|
||||
print(json.dumps(result, indent=2))
|
||||
Reference in New Issue
Block a user