File size: 5,439 Bytes
31c0f24
 
 
 
 
 
 
 
 
 
 
 
 
759e937
eb2aa42
31c0f24
 
eb2aa42
 
 
 
 
 
 
31c0f24
 
d661a05
 
31c0f24
d661a05
31c0f24
759e937
 
 
 
91d6263
759e937
 
 
 
 
 
 
31c0f24
759e937
 
 
 
 
 
 
 
 
 
 
31c0f24
d661a05
31c0f24
 
 
 
d661a05
31c0f24
 
 
 
 
d661a05
31c0f24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d661a05
31c0f24
 
 
 
 
d661a05
31c0f24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d661a05
31c0f24
 
d661a05
31c0f24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import requests
import json
import os
import time
from datetime import datetime
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class WaveDataPuller:
    def __init__(self, space_url="https://huggingface.co/spaces/nakas/NWPS_SWAN"):
        self.space_url = space_url
        self.api_url = f"{space_url}/run/predict"
        self.output_dir = os.getenv('OUTPUT_DIR', '/tmp/wave_data')
        self.poll_interval = int(os.getenv('POLL_INTERVAL', '3600'))  # 1 hour default
        
        # Create output directory with error handling for restricted environments
        try:
            os.makedirs(self.output_dir, exist_ok=True)
        except PermissionError:
            # Fall back to /tmp if we can't create in the specified location
            self.output_dir = '/tmp/wave_data'
            os.makedirs(self.output_dir, exist_ok=True)
        

    def fetch_wave_data(self, lat=40.0, lon=-74.0):
        """Fetch wave data from the Hugging Face space for a specific location."""
        try:
            logger.info(f"Fetching wave data for lat={lat}, lon={lon} from Hugging Face space...")
            
            # Construct the payload for the Gradio API call
            # Assuming the NWPS_SWAN space has a function at fn_index 0 that takes lat and lon
            payload = {
                "fn_index": 0, 
                "data": [float(lat), float(lon)]
            }
            
            headers = {"Content-Type": "application/json"}
            response = requests.post(self.api_url, headers=headers, data=json.dumps(payload))
            response.raise_for_status()  # Raise an exception for HTTP errors
            
            result = response.json()
            
            # Gradio API responses have a specific structure, usually with 'data' key
            if "data" in result and len(result["data"]) > 0:
                # Assuming the first element of the data array is the relevant wave data
                return result["data"][0]
            else:
                logger.warning(f"Unexpected API response structure: {result}")
                logger.warning("No accessible endpoints found, returning mock data")
                return self.generate_mock_data(lat, lon)
            
        except requests.exceptions.RequestException as e:
            logger.error(f"Request to Hugging Face Space failed: {e}")
            logger.warning("No accessible endpoints found, returning mock data")
            return self.generate_mock_data(lat, lon)
        except Exception as e:
            logger.error(f"Error fetching wave data: {e}")
            return None

    def generate_mock_data(self, lat=40.0, lon=-74.0):
        """Generate mock SWAN wave data for testing"""
        import random
        
        return {
            "timestamp": datetime.utcnow().isoformat(),
            "location": {"lat": lat, "lon": lon},
            "wave_data": {
                "significant_wave_height": round(random.uniform(0.5, 3.0), 2),
                "peak_wave_period": round(random.uniform(4.0, 12.0), 2),
                "wave_direction": round(random.uniform(0, 360), 1),
                "wind_speed": round(random.uniform(2.0, 15.0), 2),
                "wind_direction": round(random.uniform(0, 360), 1)
            },
            "model": "NWPS_SWAN",
            "forecast_hours": [0, 6, 12, 18, 24]
        }

    def save_data(self, data):
        """Save wave data to file"""
        if not data:
            return
        
        timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
        filename = f"wave_data_{timestamp}.json"
        filepath = os.path.join(self.output_dir, filename)
        
        try:
            with open(filepath, 'w') as f:
                json.dump(data, f, indent=2)
            logger.info(f"Data saved to {filepath}")
        except Exception as e:
            logger.error(f"Error saving data: {e}")

    def run_continuous(self, lat=40.0, lon=-74.0):
        """Run continuous data pulling"""
        logger.info(f"Starting continuous wave data pulling every {self.poll_interval} seconds")
        
        while True:
            try:
                data = self.fetch_wave_data(lat, lon)
                if data:
                    self.save_data(data)
                    logger.info("Wave data fetched and saved successfully")
                else:
                    logger.warning("No data received")
                
                time.sleep(self.poll_interval)
                
            except KeyboardInterrupt:
                logger.info("Stopping wave data puller...")
                break
            except Exception as e:
                logger.error(f"Unexpected error: {e}")
                time.sleep(60)  # Wait 1 minute before retrying

    def run_once(self, lat=40.0, lon=-74.0):
        """Run a single data pull"""
        logger.info("Fetching wave data once...")
        data = self.fetch_wave_data(lat, lon)
        if data:
            self.save_data(data)
            logger.info("Wave data fetched and saved successfully")
        else:
            logger.error("Failed to fetch wave data")

if __name__ == "__main__":
    puller = WaveDataPuller()
    
    # Check if we should run once or continuously
    run_mode = os.getenv('RUN_MODE', 'once')
    
    if run_mode.lower() == 'continuous':
        puller.run_continuous()
    else:
        puller.run_once()