File size: 5,475 Bytes
2adb41a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
ml/scripts/final_demo.py
TrackChain Capstone End-to-End Demo Walkthrough (Phase 2.7 tc.v1 SOTA).
Demonstrates the full 5-model multi-modal intelligence stack running in real-time across a 100-meter railway inspection run.
"""

import sys
import time
import json
from pathlib import Path
import numpy as np

# Add repo root to path
repo_root = Path(__file__).resolve().parent.parent.parent
if str(repo_root) not in sys.path:
    sys.path.insert(0, str(repo_root))

from ml.core.schema import (
    TrackSegment,
    DecisionType,
    SeverityLevel,
    SignalType,
    DefectClass,
)
from ml.inference.pipeline import TrackChainMLPipeline
from ml.fusion.rules import TrackChainFusionEngine


def run_capstone_demo(n_segments: int = 5):
    print("=" * 80)
    print("🚂 TrackChain Multi-Modal Edge Intelligence Stack — Live Capstone Demo")
    print("   Architecture: 5-Model Triad (YOLOv8 + PatchCore + EN13848 + Bi-LSTM + Seq-VAE)")
    print("   Contract: tc.v1 Unified [0.0, 1.0] Calibration & Cross-Modal Rule Engine")
    print("=" * 80)

    print("\n[1/4] Initializing ML Pipeline and loading models...")
    start_init = time.time()
    pipeline = TrackChainMLPipeline(
        fusion_engine=TrackChainFusionEngine(persistence_window=2, known_threshold=0.50, novel_threshold=0.50),
        conditional_typing=False,
    )
    print(f"      Pipeline initialized in {(time.time() - start_init):.2f}s")

    print("\n[2/4] Generating synthetic 100m railway inspection run (5 segments)...")
    segments = []
    scenarios = [
        ("Clean Track", False, 0.0, "mainline_standard"),
        ("Missing Fastener (Visual Known)", True, 0.0, "mainline_standard"),
        ("Clean Track (Post-Repair)", False, 0.0, "mainline_standard"),
        ("Track Twist Exceedance (Geometry Known)", False, 9.5, "mainline_standard"),
        ("Compound Defect: Crack + Gauge Spread (Dual Stream)", True, 14.0, "mainline_high_speed"),
    ]

    for idx, (scenario_name, has_visual_defect, vert_dip_mm, sec_type) in enumerate(scenarios):
        start_m = idx * 20.0
        end_m = (idx + 1) * 20.0
        n_bins = 80

        # Frame
        frame = np.zeros((480, 640, 3), dtype=np.uint8)
        frame[200:280, :] = 120
        if has_visual_defect:
            frame[220:260, 300:340] = 255  # Defect

        # Telemetry
        vertical = np.random.normal(0.0, 0.2, n_bins)
        if vert_dip_mm > 0:
            vertical[30:50] = vert_dip_mm

        gauge = np.full(n_bins, 1676.0)
        if idx == 4:
            gauge[35:45] = 1684.0  # Gauge widening +8mm

        telemetry = {
            "roll_rad": np.zeros(n_bins),
            "lateral_pos_mm": np.random.normal(0.0, 0.3, n_bins),
            "vertical_pos_mm": vertical,
            "gauge_mm": gauge,
        }

        seg = TrackSegment(
            segment_id=f"seg-{int(start_m):04d}-{int(end_m):04d}",
            chainage_start_m=start_m,
            chainage_end_m=end_m,
            frames=[frame],
            telemetry=telemetry,
            section_type=sec_type,
        )
        segments.append((scenario_name, seg))

    print(f"      Created {len(segments)} test segments.")

    print("\n[3/4] Processing live inspection stream across all 5 models & fusion...")
    print("-" * 80)
    print(f"{'Chainage (m)':<15} | {'Scenario':<25} | {'Decision':<14} | {'Severity':<9} | {'Latency':<8}")
    print("-" * 80)

    demo_results = []
    total_time = 0.0

    for scenario_name, seg in segments:
        t0 = time.perf_counter()
        decision, signals = pipeline.process_segment(seg)
        dt_ms = (time.perf_counter() - t0) * 1000.0
        total_time += dt_ms

        chainage_str = f"{seg.chainage_start_m:.1f}m - {seg.chainage_end_m:.1f}m"
        print(f"{chainage_str:<15} | {scenario_name:<25} | {decision.decision.value:<14} | {decision.severity.value:<9} | {dt_ms:>5.1f} ms")

        prim_def = decision.primary_defect
        prim_def_str = prim_def.value if hasattr(prim_def, "value") else str(prim_def or "none")

        demo_results.append({
            "chainage": chainage_str,
            "scenario": scenario_name,
            "decision": decision.decision.value if hasattr(decision.decision, "value") else str(decision.decision),
            "severity": decision.severity.value if hasattr(decision.severity, "value") else str(decision.severity),
            "confidence": round(float(decision.confidence), 4),
            "primary_defect": prim_def_str,
            "action": getattr(decision, "action", "Routine inspection cycle maintained."),
            "latency_ms": round(dt_ms, 2),
        })

    avg_ms = total_time / len(segments)
    fps = 1000.0 / avg_ms if avg_ms > 0 else 0.0

    print("-" * 80)
    print(f"[4/4] Demo Summary: Average Pipeline Latency = {avg_ms:.1f} ms ({fps:.1f} FPS, Target >= 15 FPS)")
    print("=" * 80)

    # Save summary
    out_dir = repo_root / "artifacts"
    out_dir.mkdir(parents=True, exist_ok=True)
    summary_path = out_dir / "demo_run_summary.json"
    with open(summary_path, "w", encoding="utf-8") as f:
        json.dump({
            "schema_version": "tc.v1",
            "average_latency_ms": round(avg_ms, 2),
            "fps": round(fps, 1),
            "segments": demo_results,
            "status": "PASSED",
        }, f, indent=2)

    print(f"\n[OK] Capstone Demo completed successfully. Report saved to: {summary_path}")
    return 0


if __name__ == "__main__":
    sys.exit(run_capstone_demo())