File size: 6,285 Bytes
47ffedc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e9f9d83
47ffedc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
"""
SWaT ML Inference API Service
Directly calls ml_inference.run_pipeline() and returns dashboard-friendly JSON
"""

from flask import Flask, request, jsonify
from flask_cors import CORS
import os
import sys
import time
import traceback

# Import from current folder
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

app = Flask(__name__)
CORS(app)

ML_AVAILABLE = False
ML_IMPORT_ERROR = None
ML_API_FILE = os.path.abspath(__file__)
ML_PID = os.getpid()

sensor_buffer = None
MODELS = None  # cached models (already cached in your ml_inference too)


# -----------------------------------------------------------------------------
# Startup: load models ONCE + create one shared buffer
# -----------------------------------------------------------------------------
try:
    from sensor_buffer import SensorBuffer
    import ml_inference

    t0 = time.perf_counter()
    MODELS = ml_inference.load_models()
    t1 = time.perf_counter()

    if not MODELS or not MODELS.get("loaded"):
        raise RuntimeError(MODELS.get("error", "Models not loaded"))

    sensor_buffer = SensorBuffer(window_size=60, n_features=40)
    ML_AVAILABLE = True

    print(f"✅ Models loaded successfully in {(t1 - t0)*1000:.1f} ms")
    print("ML Available: True")

except Exception as e:
    ML_IMPORT_ERROR = f"{e}\n{traceback.format_exc()}"
    ML_AVAILABLE = False
    print(f"ML Import Error: {e}")
    print(traceback.format_exc())
    print("ML Available: False")


# -----------------------------------------------------------------------------
# Map ml_inference output -> what your web app expects
# -----------------------------------------------------------------------------
def to_dashboard_schema(result: dict):
    stage1 = result.get("stage1", {}) or {}
    stage2 = result.get("stage2", {}) or {}
    stage3 = result.get("stage3", {}) or {}

    bs = result.get("buffer_status", {}) or {}
    buffer_status = {
        "ready": bool(bs.get("ready", sensor_buffer.is_ready() if sensor_buffer else False)),
        "size": int(bs.get("size", len(sensor_buffer) if sensor_buffer else 0)),
        "capacity": int(getattr(sensor_buffer, "window_size", 60)) if sensor_buffer else 60,
        "usingBuffer": bool(bs.get("using_buffer", False)),
    }

    return {
        "success": True,
        "stage1": {
            "isAnomaly": bool(stage1.get("is_anomaly", False)),
            "confidence": float(stage1.get("confidence", 0.0)),
            "score": stage1.get("score", None),
            "modelType": stage1.get("model_type", None),
        },
        "stage2": {
            "state": stage2.get("state", "UNKNOWN"),
            "confidence": float(stage2.get("confidence", 0.0)),
            "modelType": stage2.get("model_type", None),
        },
        "stage3": {
            "component": stage3.get("component", None),
            "confidence": float(stage3.get("confidence", 0.0)),
            "top3": [
                {"component": c, "confidence": float(p)}
                for (c, p) in (stage3.get("top3") or [])
            ],
            "modelType": stage3.get("model_type", None),
        },
        "componentHealth": result.get("component_health", {}) or {},
        "recommendedActions": result.get("actions", []) or [],
        "alertsSent": result.get("alerts_sent", {}) or {},
        "bufferStatus": buffer_status,
    }


# -----------------------------------------------------------------------------
# Routes
# -----------------------------------------------------------------------------
@app.route("/health", methods=["GET", "POST", "HEAD"])
def health():
    return jsonify({
        "status": "ok",
        "ml_available": bool(ML_AVAILABLE),
        "pid": ML_PID,
        "file": ML_API_FILE,
        "import_error": ML_IMPORT_ERROR,
        "buffer_size": len(sensor_buffer) if sensor_buffer else 0,
        "buffer_ready": sensor_buffer.is_ready() if sensor_buffer else False,
    })


@app.route("/api/inference", methods=["POST"])
def run_inference():
    if not ML_AVAILABLE:
        return jsonify({
            "success": False,
            "error": "ML components not available",
            "import_error": ML_IMPORT_ERROR
        }), 503

    try:
        data = request.get_json(silent=True) or {}
        payload = data.get("payload")

        if not isinstance(payload, dict):
            return jsonify({"success": False, "error": "Missing payload in request"}), 400

        # ✅ Directly call YOUR working pipeline
        result = ml_inference.run_pipeline(payload, sensor_buffer=sensor_buffer)

        if not result.get("success", False):
            return jsonify({
                "success": False,
                "error": result.get("error", "Pipeline failed"),
                "bufferStatus": {
                    "ready": sensor_buffer.is_ready() if sensor_buffer else False,
                    "size": len(sensor_buffer) if sensor_buffer else 0,
                    "capacity": getattr(sensor_buffer, "window_size", 60) if sensor_buffer else 60,
                    "usingBuffer": False,
                }
            }), 200

        return jsonify(to_dashboard_schema(result)), 200

    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


@app.route("/api/buffer/reset", methods=["POST"])
def reset_buffer():
    global sensor_buffer
    if not ML_AVAILABLE:
        return jsonify({"success": False, "error": "ML not available"}), 503

    from sensor_buffer import SensorBuffer
    sensor_buffer = SensorBuffer(window_size=60, n_features=40)
    return jsonify({"success": True, "message": "Buffer reset"}), 200


@app.route("/api/buffer/status", methods=["GET"])
def buffer_status():
    if not sensor_buffer:
        return jsonify({"ready": False, "size": 0, "capacity": 60})
    return jsonify({
        "ready": sensor_buffer.is_ready(),
        "size": len(sensor_buffer),
        "capacity": sensor_buffer.window_size
    })


if __name__ == "__main__":
    print("=" * 60)
    print("SWaT ML Inference API Service")
    print("=" * 60)
    print(f"ML Available: {ML_AVAILABLE}")
    print("Starting server on http://localhost:5000")
    print("=" * 60)

    app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))