Back to News/Case Study: Minerva Digital Platform — Petrosea

Case Study: Minerva Digital Platform — Petrosea

Faisal Affan
2/23/2026
Case Study: Minerva Digital Platform — Petrosea — 1 / 5
1 / 5

Engineering Case Study: Petrosea Minerva

"In mining, every second of equipment downtime translates to massive revenue loss. Data is the new ore."

Context

Sebagai bagian dari inisiatif transformasi digital di Petrosea, saya berkontribusi dalam perancangan dan pengembangan backend engineering untuk Minerva Digital Platform. Platform ini menghubungkan sensor IoT di alat berat (heavy equipment/fleet), menganalisis metrik operasional, dan menghadirkan real-time dispatch serta analitik kepada tim operasional di site tambang.

Minerva adalah pusat dari Operational Excellence di area pertambangan, mengubah cara data dikumpulkan dari manual (berbasis kertas/radio) menjadi otomatis, near real-time, dan data-driven.

Artikel ini membahas tantangan teknis dalam membangun sistem yang sanggup menangani tingginya volume data telemetri di lingkungan dengan konektivitas yang sangat terbatas.


🏔️ 1. The Challenge

Kondisi Lapangan Tambang

Membangun solusi digital untuk industri pertambangan sangat berbeda dengan aplikasi urban enterprise:

Konektivitas Ekstrim

Area tambang sering kali berada di remote area dengan sinyal internet yang tidak stabil (blank spot), mengharuskan sistem bekerja offline-first.

Volume Data Tinggi

Ratusan alat berat mengirimkan data telemetri (GPS, fuel level, engine temp, payload) setiap detik — puluhan ribu data points per menit.

Misi Kritikal

Data harus diolah real-time untuk Fleet Management dan Predictive Maintenance. Delay deteksi anomali mesin bisa menyebabkan kerusakan bernilai miliaran rupiah.

Integrasi Sensor

Menyatukan berbagai format data dari sensor OEM (Original Equipment Manufacturer) yang bermacam-macam ke satu standar platform internal.

Tantangan Engineering

  1. Offline-First di Edge: Perangkat di alat berat harus buffer data saat tidak ada sinyal dan sync saat koneksi tersedia — tanpa kehilangan satu data point pun.
  2. Thundering Herd: Saat puluhan truk keluar dari blank spot secara bersamaan, semua device akan flood data historis ke server secara serentak.
  3. Heterogeneous Sensors: Sensor dari berbagai vendor (Caterpillar, Komatsu, Hitachi) mengirim data dalam format berbeda — harus dinormalisasi ke satu schema.
  4. Sub-Second Alerting: Geofence breach, overspeed, dan anomali mesin harus terdeteksi dalam hitungan milidetik, bukan detik.
  5. Harsh Environment: Hardware di tambang menghadapi debu, getaran, dan suhu ekstrim — reliability software harus mengkompensasi hardware yang rentan gagal.

Business Impact

Sebelum Minerva, estimasi 12-15% waktu operasional hilang karena unplanned downtime dan routing yang tidak efisien. Setiap jam downtime excavator di site tambang bernilai Rp 15-25 juta — belum termasuk efek domino ke truk yang menunggu di loading area.


🏗️ 2. Architecture Overview

Arsitektur Minerva dirancang dengan prinsip Edge-to-Cloud dan Event-Driven Architecture untuk memastikan resiliency data di tengah koneksi yang terputus-putus.

High-Level Architecture

Data Flow: Edge to Cloud

Key Architecture Decisions:


🛠️ 3. Technology Stack

TechnologyPeranAlasan Dipilih
GolangCore backend servicesGoroutines mampu handle puluhan ribu koneksi TCP/MQTT concurrent dengan memory footprint minimal
Apache KafkaCloud-side message brokerHigh-throughput, durable, replayable event streaming
gRPCInter-service communicationLow-latency, strongly-typed, efficient binary serialization
RedisReal-time state cacheCaching status terakhir kendaraan (lokasi, status, fuel) untuk dashboard real-time
TechnologyPeranAlasan Dipilih
MQTT (Mosquitto)Edge protocolLightweight, designed untuk koneksi high latency / low bandwidth
SQLite (WAL mode)Edge local bufferReliable offline storage, tahan sudden power loss
Embedded LinuxGateway OSLightweight, customizable, long-term support
Protocol BuffersEdge-to-cloud serialization3-10x lebih kecil dari JSON, hemat bandwidth
TechnologyPeranAlasan Dipilih
InfluxDBTime-series databaseKompresi efisien untuk data telemetri, query temporal cepat
PostgreSQLMaster data & geofenceACID compliance untuk data referensi, PostGIS untuk spatial queries
Apache SparkBatch analyticsProcessing historical data untuk reporting dan trend analysis
Python (scikit-learn)Predictive maintenance MLModel anomaly detection untuk engine health prediction
TechnologyPeranAlasan Dipilih
ReactDashboard web appComponent-based, rich ecosystem untuk data visualization
Mapbox GLReal-time fleet mapHigh-performance WebGL map rendering untuk ratusan marker bergerak
WebSocketReal-time updatesPush-based updates untuk dashboard tanpa polling
D3.js / RechartsAnalytics chartsFlexible data visualization untuk time-series dan aggregation

Telemetry Payload Schema

Prop

Type


📁 4. Project Structure

main.go
handler.go
deduplicator.go
normalizer.go
main.go
evaluator.go
geofence.go
alerter.go
main.go
handlers/
middleware/
telemetry.proto
fleet.proto
alert.proto

🔍 5. Feature Deep Dives

Feature #1: Telemetry Ingestion & Deduplication

Problem: Saat truk keluar dari area blank spot, Gateway di truk akan mengirimkan ("flood") semua data historis yang sempat tertahan selama offline ke server, bersamaan dengan data real-time saat itu. Hal ini memicu thundering herd problem dan risiko duplikasi data.

Solution: Pipeline ingestion asinkron yang mampu scaling otomatis menyerap spike traffic dan mekanisme idempotency berdasarkan device_id + timestamp.

services/ingestion/handler.go
// Simplified Telemetry Ingestion Service
type TelemetryPayload struct {
    DeviceID    string    `json:"device_id"`
    Timestamp   time.Time `json:"timestamp"`
    Latitude    float64   `json:"lat"`
    Longitude   float64   `json:"lng"`
    Speed       float64   `json:"speed"`
    FuelLevel   float64   `json:"fuel_level"`
    EngineTemp  float64   `json:"engine_temp"`
    OilPressure float64   `json:"oil_pressure"`
    Payload     float64   `json:"payload_tonnage"`
}

func (s *IngestionService) ProcessBatch(ctx context.Context, payloads []TelemetryPayload) error {
    // 1. Sort by timestamp to ensure chronological order
    sort.Slice(payloads, func(i, j int) bool {
        return payloads[i].Timestamp.Before(payloads[j].Timestamp)
    })

    // 2. Bloom Filter + Redis idempotency check
    uniquePayloads := s.deduplicator.FilterUnique(ctx, payloads)
    if len(uniquePayloads) == 0 {
        return nil // All duplicates — skip
    }

    // 3. Batch insert to Time-Series DB
    if err := s.tsdb.InsertBatch(ctx, uniquePayloads); err != nil {
        return fmt.Errorf("failed to insert batch telemetry: %w", err)
    }

    // 4. Update "Current State" in Redis for real-time dashboard
    latest := getLatestPerDevice(uniquePayloads)
    for deviceID, payload := range latest {
        s.cache.Set(ctx, fmt.Sprintf("fleet_state:%s", deviceID), payload, 0)
    }

    // 5. Publish processed events for downstream consumers
    for _, p := range uniquePayloads {
        s.kafka.Publish("processed-telemetry", p)
    }

    s.metrics.IngestCounter.Add(float64(len(uniquePayloads)))
    return nil
}

Deduplication Strategy:

services/ingestion/deduplicator.go
// Bloom Filter: O(1) probabilistic check — 99.9% accurate
func (d *Deduplicator) MayExist(deviceID string, ts time.Time) bool {
    key := fmt.Sprintf("%s:%d", deviceID, ts.UnixNano())
    return d.bloom.Test([]byte(key))
}

Bloom filter memberikan false positive rate < 0.1% dengan memory footprint hanya ~2MB untuk 10 juta keys. Digunakan sebagai first-pass filter untuk menghindari roundtrip ke Redis.

services/ingestion/deduplicator.go
// Redis: exact dedup check untuk data yang lolos bloom filter
func (d *Deduplicator) IsExactDuplicate(ctx context.Context, deviceID string, ts time.Time) bool {
    key := fmt.Sprintf("dedup:%s:%d", deviceID, ts.UnixNano())
    exists, _ := d.redis.SetNX(ctx, key, 1, 24*time.Hour).Result()
    return !exists // SetNX returns false if key already exists
}

Redis digunakan sebagai second-pass dengan TTL 24 jam — cukup untuk window deduplication karena data edge buffer maksimal 12 jam.

services/ingestion/deduplicator.go
// Combined: Bloom Filter (fast) → Redis (exact)
func (d *Deduplicator) FilterUnique(ctx context.Context, payloads []TelemetryPayload) []TelemetryPayload {
    var unique []TelemetryPayload
    for _, p := range payloads {
        // Fast path: bloom filter says "definitely not seen"
        if !d.MayExist(p.DeviceID, p.Timestamp) {
            d.bloom.Add([]byte(fmt.Sprintf("%s:%d", p.DeviceID, p.Timestamp.UnixNano())))
            unique = append(unique, p)
            continue
        }
        // Slow path: bloom says "maybe seen" → check Redis
        if !d.IsExactDuplicate(ctx, p.DeviceID, p.Timestamp) {
            unique = append(unique, p)
        }
    }
    return unique
}

Pendekatan dua lapis ini mengurangi Redis calls sebesar ~95% saat bulk sync, karena mayoritas data baru langsung lolos bloom filter.

Results:

MetricSebelumSesudahImprovement
Duplicate data rate~18% (saat bulk sync)< 0.01%↓ 99.9%
Ingestion throughput~5,000 events/sec~50,000 events/sec↑ 10x
Avg ingestion latency800ms< 50ms↓ 94%
Data loss during bulk sync~3%0%↓ 100%

Feature #2: Dynamic Geofencing & Alerting

Problem: Operator perlu diberitahu seketika jika sebuah dump truck memasuki area terlarang, keluar dari jalur jalan tambang, atau melebihi batas kecepatan di zona tertentu. Aturan ini dinamis; zona bisa berubah seiring aktivitas peledakan (blasting) atau cuaca.

Solution: Menggunakan algoritma Ray-Casting (Point in Polygon) di backend yang dieksekusi secara in-memory untuk setiap payload location yang masuk.

services/rule-engine/geofence.go
// Ray-Casting Point in Polygon — O(n) per polygon edge
func isPointInPolygon(point Point, polygon []Point) bool {
    inPolygon := false
    j := len(polygon) - 1
    for i := 0; i < len(polygon); i++ {
        if (polygon[i].Y < point.Y && polygon[j].Y >= point.Y ||
            polygon[j].Y < point.Y && polygon[i].Y >= point.Y) &&
            (polygon[i].X <= point.X || polygon[j].X <= point.X) {
            if polygon[i].X+(point.Y-polygon[i].Y)/
                (polygon[j].Y-polygon[i].Y)*(polygon[j].X-polygon[i].X) < point.X {
                inPolygon = !inPolygon
            }
        }
        j = i
    }
    return inPolygon
}

// Evaluate all geofence rules for a single telemetry point
func (e *GeofenceEvaluator) Evaluate(ctx context.Context, payload TelemetryPayload) []Alert {
    point := Point{X: payload.Longitude, Y: payload.Latitude}
    var alerts []Alert

    for _, zone := range e.zones.GetAll() {
        inside := isPointInPolygon(point, zone.Polygon)

        switch zone.Type {
        case "restricted":
            if inside {
                alerts = append(alerts, Alert{
                    DeviceID: payload.DeviceID,
                    Type:     "ZONE_BREACH",
                    Severity: "CRITICAL",
                    Message:  fmt.Sprintf("Unit %s entered restricted zone: %s", payload.DeviceID, zone.Name),
                    Location: point,
                })
            }
        case "speed_limit":
            if inside && payload.Speed > zone.SpeedLimit {
                alerts = append(alerts, Alert{
                    DeviceID: payload.DeviceID,
                    Type:     "OVERSPEED",
                    Severity: "WARNING",
                    Message:  fmt.Sprintf("Unit %s: %.1f km/h in %.0f km/h zone (%s)",
                        payload.DeviceID, payload.Speed, zone.SpeedLimit, zone.Name),
                    Location: point,
                })
            }
        }
    }
    return alerts
}

Dynamic Zone Updates

Zone geofence bisa di-update tanpa restart service. Dispatcher cukup menggambar polygon baru di dashboard map, dan Rule Engine akan me-reload zone config dari Redis dalam hitungan detik. Ini krusial saat terjadi blasting activity — area peledakan harus di-restricted seketika.

Results:

MetricSebelum (Manual Radio)Sesudah (Minerva)Improvement
Geofence breach detection time5-15 menit (visual spotting)< 2 detik↓ 99%
Overspeed incidents~45 per bulan~12 per bulan↓ 73%
Restricted zone violations~8 per bulan~1 per bulan↓ 87%
Safety incident rateBaseline↓ 35%Significant

Feature #3: Real-Time Fleet Dispatch & Cycle Time Optimization

Problem: Dispatcher mengandalkan radio dan visual monitoring untuk mengarahkan truk ke loading area. Akibatnya, truk sering mengantre di satu area sementara area lain kosong — membuang waktu dan bahan bakar.

Solution: Real-time fleet dispatch system yang mengoptimasi assignment truk ke excavator berdasarkan posisi, antrean, dan cycle time historis.

services/fleet-api/dispatch.go
// Fleet Dispatch Optimizer
type DispatchDecision struct {
    TruckID      string  `json:"truck_id"`
    ExcavatorID  string  `json:"excavator_id"`
    EstimatedETA float64 `json:"estimated_eta_minutes"`
    QueuePosition int    `json:"queue_position"`
    Route        []Point `json:"route"`
}

func (d *DispatchService) OptimizeAssignment(ctx context.Context, truckID string) (*DispatchDecision, error) {
    // 1. Get current truck position
    truckState, err := d.cache.Get(ctx, fmt.Sprintf("fleet_state:%s", truckID))
    if err != nil {
        return nil, fmt.Errorf("truck state not found: %w", err)
    }

    // 2. Get all available excavators and their queue lengths
    excavators, err := d.getActiveExcavators(ctx)
    if err != nil {
        return nil, err
    }

    // 3. Score each excavator based on multi-factor optimization
    var bestScore float64
    var bestExcavator *Excavator
    for _, exc := range excavators {
        score := d.calculateScore(truckState, exc)
        if score > bestScore {
            bestScore = score
            bestExcavator = &exc
        }
    }

    // 4. Build dispatch decision
    return &DispatchDecision{
        TruckID:       truckID,
        ExcavatorID:   bestExcavator.ID,
        EstimatedETA:  calculateETA(truckState.Location, bestExcavator.Location),
        QueuePosition: bestExcavator.QueueLength + 1,
    }, nil
}

// Multi-factor scoring: distance (40%), queue length (35%), cycle time (25%)
func (d *DispatchService) calculateScore(truck *FleetState, exc Excavator) float64 {
    distance := haversineDistance(truck.Location, exc.Location)
    distScore := 1.0 / (1.0 + distance)                    // Closer = better
    queueScore := 1.0 / (1.0 + float64(exc.QueueLength))   // Shorter queue = better
    cycleScore := 1.0 / (1.0 + exc.AvgCycleTime)           // Faster cycle = better

    return (distScore * 0.40) + (queueScore * 0.35) + (cycleScore * 0.25)
}

Results:

MetricSebelum (Radio Dispatch)Sesudah (Minerva)Improvement
Avg cycle time32 menit24 menit↓ 25%
Queue time at loading area8-12 menit3-5 menit↓ 58%
Truck utilization rate68%82%↑ 21%
Fuel waste (idle)Baseline↓ 18%Significant

Feature #4: Predictive Maintenance Engine

Problem: Kerusakan mesin alat berat bersifat catastrophic — biaya perbaikan engine excavator bisa mencapai Rp 2-5 miliar per unit. Tim maintenance mengandalkan jadwal servis berkala (time-based), bukan kondisi aktual mesin (condition-based).

Solution: Anomaly detection engine yang menganalisis pola telemetri mesin (engine temperature, oil pressure, vibration) untuk mendeteksi degradasi sebelum terjadi kegagalan.

services/rule-engine/maintenance_rules.go
// Engine Health Rules - evaluated in real-time
type EngineHealthRule struct {
    Name       string
    Condition  func(current, avg30d TelemetrySummary) bool
    Severity   string
    Message    string
}

var engineHealthRules = []EngineHealthRule{
    {
        Name: "engine_temp_anomaly",
        Condition: func(current, avg30d TelemetrySummary) bool {
            // Temperature > 2 standard deviations above 30-day average
            return current.EngineTemp > (avg30d.EngineTemp + 2*avg30d.EngineTempStdDev)
        },
        Severity: "WARNING",
        Message:  "Engine temperature trending above normal range",
    },
    {
        Name: "oil_pressure_drop",
        Condition: func(current, avg30d TelemetrySummary) bool {
            // Oil pressure dropped > 20% from 30-day average
            dropPercent := (avg30d.OilPressure - current.OilPressure) / avg30d.OilPressure * 100
            return dropPercent > 20
        },
        Severity: "CRITICAL",
        Message:  "Oil pressure significantly below normal — potential engine failure risk",
    },
    {
        Name: "fuel_consumption_spike",
        Condition: func(current, avg30d TelemetrySummary) bool {
            // Fuel consumption > 30% above average indicates engine inefficiency
            return current.FuelRate > (avg30d.FuelRate * 1.30)
        },
        Severity: "INFO",
        Message:  "Fuel consumption above normal — schedule efficiency check",
    },
}

func (e *MaintenanceEvaluator) EvaluateHealth(ctx context.Context, deviceID string, current TelemetrySummary) []Alert {
    avg30d, err := e.tsdb.Get30DayAverage(ctx, deviceID)
    if err != nil {
        return nil // Insufficient historical data
    }

    var alerts []Alert
    for _, rule := range engineHealthRules {
        if rule.Condition(current, avg30d) {
            alerts = append(alerts, Alert{
                DeviceID: deviceID,
                Type:     "MAINTENANCE",
                Severity: rule.Severity,
                Message:  rule.Message,
                Metadata: map[string]interface{}{
                    "current_temp":     current.EngineTemp,
                    "avg_30d_temp":     avg30d.EngineTemp,
                    "current_pressure": current.OilPressure,
                    "avg_30d_pressure": avg30d.OilPressure,
                    "engine_hours":     current.EngineHours,
                },
            })
        }
    }
    return alerts
}

Preventing Catastrophic Failure

Dalam satu insiden nyata, sistem mendeteksi penurunan tekanan oli 23% pada sebuah excavator Komatsu PC2000 48 jam sebelum oli habis total. Tanpa deteksi dini, engine replacement berbiaya Rp 3.2 miliar kemungkinan besar tidak terhindarkan. Deteksi dini ini saja sudah mem-payback biaya pengembangan seluruh modul predictive maintenance.

Results:

MetricSebelum (Time-Based)Sesudah (Condition-Based)Improvement
Unplanned downtime~180 jam/bulan~65 jam/bulan↓ 64%
Early failure detection rate~15%~78%↑ 420%
Maintenance cost per unitBaseline↓ 22%Significant
Mean Time To Repair (MTTR)8 jam4.5 jam↓ 44%

Feature #5: Edge-to-Cloud Sync Pipeline

Problem: Gateway di setiap kendaraan harus mampu beroperasi secara mandiri selama berjam-jam tanpa koneksi, lalu melakukan sinkronisasi yang efisien saat koneksi tersedia — tanpa membanjiri server.

Solution: Adaptive sync pipeline dengan rate limiting dan prioritization di sisi edge.

edge-gateway/sync_manager.go
// Adaptive Sync Manager — prioritizes critical data
type SyncManager struct {
    buffer    *SQLiteBuffer
    mqtt      *MQTTClient
    maxBatch  int
    backoff   *ExponentialBackoff
}

func (s *SyncManager) SyncLoop(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return nil
        default:
            if !s.mqtt.IsConnected() {
                time.Sleep(5 * time.Second)
                continue
            }

            // Phase 1: Send critical alerts immediately (MQTT QoS 2)
            alerts, _ := s.buffer.GetUnsyncedByPriority(ctx, "critical", 10)
            for _, alert := range alerts {
                if err := s.mqtt.PublishQoS2(ctx, "telemetry/alert", alert); err != nil {
                    break // Connection lost — will retry
                }
                s.buffer.MarkSynced(ctx, alert.ID)
            }

            // Phase 2: Batch send recent data (last 30 min)
            recent, _ := s.buffer.GetUnsyncedRecent(ctx, 30*time.Minute, s.maxBatch)
            if len(recent) > 0 {
                batch := encodeBatch(recent) // Protocol Buffers encoding
                if err := s.mqtt.PublishQoS1(ctx, "telemetry/batch", batch); err != nil {
                    s.backoff.Wait()
                    continue
                }
                s.buffer.MarkBatchSynced(ctx, recent)
                s.backoff.Reset()
            }

            // Phase 3: Throttled historical data
            historical, _ := s.buffer.GetUnsyncedOld(ctx, s.maxBatch/2)
            if len(historical) > 0 {
                batch := encodeBatch(historical)
                s.mqtt.PublishQoS1(ctx, "telemetry/batch", batch)
                s.buffer.MarkBatchSynced(ctx, historical)
                time.Sleep(500 * time.Millisecond) // Throttle to avoid flooding
            }

            time.Sleep(1 * time.Second)
        }
    }
}

Results:

MetricSebelumSesudahImprovement
Data loss during offline period~5%0%↓ 100%
Avg sync time (after reconnect)3-5 menit< 30 detik↓ 90%
Bandwidth usage per sync4.2 MB890 KB (Protobuf)↓ 79%
Server overload during bulk syncFrequentZero↓ 100%

🚢 6. Implementation & Deployment

Phase 1: Edge Gateway Development

Membangun firmware edge gateway yang mampu membaca sensor via CAN bus / J1939 protocol, menyimpan data ke SQLite buffer, dan berkomunikasi via MQTT. Termasuk testing di lingkungan dengan simulated packet loss hingga 60%.

Phase 2: Cloud Infrastructure Setup

Setup Kafka cluster, InfluxDB, PostgreSQL, dan Redis di cloud/on-premise. Konfigurasi Kafka topic partitioning berdasarkan site_id untuk memastikan data dari satu site diproses secara ordered. Deploy ingestion service dengan horizontal auto-scaling.

Phase 3: Rule Engine & Alerting

Implementasi geofence evaluator, speed limit checker, dan engine health rules. Integrasi dengan WebSocket server untuk push real-time ke dashboard. Setup alert routing: critical → dispatcher + siren, warning → dashboard, info → analytics log.

Phase 4: Dashboard & Dispatch UI

Pengembangan dashboard real-time dengan live fleet map (Mapbox), cycle time analytics, dan maintenance scheduler. Mobile-responsive untuk tablet yang digunakan dispatcher di control room.

Phase 5: Predictive Maintenance Model

Training anomaly detection model menggunakan 6 bulan data historis dari maintenance logs dan telemetry records. Deploy inference server sebagai gRPC service yang dikonsumsi oleh Rule Engine.

Phase 6: Site Rollout & Calibration

Rollout bertahap per site — dimulai dari 1 site pilot dengan 30 unit, kemudian expand ke 3 site dengan 150+ unit. Calibration threshold per tipe equipment karena karakteristik mesin Caterpillar berbeda dengan Komatsu.


🚀 7. Overall Business Impact

Implementasi platform Minerva memberikan dampak operasional langsung:

Key Numbers

  • Unplanned downtime: turun 64% (dari ~180 jam → ~65 jam/bulan) - Cycle time: turun 25% (dari 32 menit → 24 menit) - Fuel efficiency: meningkat 18% melalui optimasi idle time dan routing - Safety incidents: turun 35% melalui geofence dan speed monitoring - Maintenance cost: turun 22% per unit melalui condition-based maintenance - Truck utilization: naik dari 68%82% (+21%)

ROI Summary

#InitiativeBeforeAfterAnnual Value
1Cycle time optimization32 min avg24 min avgRp 8.4M/truk/bulan (lebih banyak trip)
2Predictive maintenance180 jam unplanned downtime65 jamRp 1.7 miliar/tahun (prevented failures)
3Fuel efficiencyBaseline idle waste-18% fuelRp 420 juta/tahun per site
4Safety compliance45 overspeed/bulan12 overspeed/bulanPriceless (nyawa manusia)

🏢 8. Post-Launch: Tantangan & Evolusi

8.1 Sensor Heterogeneity Nightmare

Salah satu tantangan terbesar post-launch adalah keragaman sensor di armada:

VendorProtocolData FormatTantangan
CaterpillarCAT Product LinkProprietary JSONMemerlukan adapter khusus + NDA untuk akses API
KomatsuKOMTRAXBinary via satelliteLatensi tinggi (15-30 detik), format berbeda per model
HitachiConSiteREST APIRate limit ketat, perlu credential management
Aftermarket GPSMQTT / TCPNMEA 0183Murah tapi kurang reliable, parsing manual

Solusi yang diterapkan: Membangun Normalizer Layer — setiap vendor memiliki adapter sendiri yang mentransformasi data ke unified TelemetryPayload schema. Adapter baru bisa di-deploy tanpa mengubah core ingestion pipeline.

8.2 Konektivitas di Remote Site

The Satellite Fallback

Di beberapa site yang sangat terpencil, bahkan sinyal seluler pun tidak tersedia. Kami menggunakan VSAT (Very Small Aperture Terminal) sebagai fallback, tetapi bandwidth-nya sangat terbatas (512 kbps shared). Protocol Buffers dan delta compression menjadi penyelamat — mengurangi payload size hingga 79% dibanding JSON mentah.

8.3 Hardware Reliability di Lingkungan Tambang

Edge gateway yang dipasang di alat berat menghadapi kondisi ekstrim:

  • Debu tebal — menyebabkan overheating dan kegagalan kipas pendingin
  • Getaran konstan — konektor bisa longgar, menyebabkan intermittent connection
  • Suhu 45°C+ — di kabin excavator tanpa AC
  • Power fluctuation — saat engine start/stop, voltage bisa spike

Solusi hardware: industrial-grade enclosure (IP67), vibration-dampened mounting, wide-voltage power supply (9-36V DC), dan watchdog timer yang auto-restart gateway jika hang.


🎓 9. Lessons Learned

  • Edge-first architecture dari hari pertama — data tidak pernah hilang meskipun koneksi terputus berjam-jam.
  • Golang terbukti ideal: satu ingestion service instance mampu handle 50,000 events/sec dengan memory < 200MB.
  • Event-driven architecture (Kafka) memudahkan penambahan consumer baru tanpa mengubah pipeline yang sudah berjalan.
  • Bloom filter + Redis deduplication strategy menghilangkan masalah duplikasi yang sebelumnya sangat mengganggu akurasi analytics.
  • In-memory rule evaluation memberikan sub-second alerting — kritis untuk keselamatan operasi tambang.
  • Condition-based maintenance mengubah paradigma dari "ganti jadwal" menjadi "ganti saat perlu" — penghematan signifikan.
  • Gunakan Apache Flink sejak awal untuk complex event processing (CEP) — rule engine custom works, tapi Flink memberikan windowing dan state management yang lebih mature.
  • Invest lebih awal di automated E2E testing untuk edge-to-cloud pipeline — testing di lab tidak pernah 100% mereplikasi kondisi lapangan.
  • Mulai dengan ClickHouse selain InfluxDB untuk analytics queries yang lebih kompleks — InfluxDB sangat baik untuk time-series tapi kurang fleksibel untuk ad-hoc analytics.
  • gRPC dari hari pertama untuk semua inter-service communication — JSON REST API yang awalnya dipakai menjadi bottleneck saat traffic naik.
  • Standardisasi hardware gateway lebih agresif — terlalu banyak variasi hardware menyulitkan firmware management.
  • Chaos engineering di staging environment — simulate network partition, broker failure, dan database degradation secara reguler.

Technical Debt yang Masih Ada

#DebtImpactPriority
1ML model retraining masih semi-manualModel bisa stale jika pola operasi berubahHigh
2Belum ada circuit breaker di edge-to-cloud syncJika cloud down lama, buffer bisa penuhMedium
3Dashboard belum fully mobile-responsiveDispatcher di lapangan harus pakai tabletMedium
4Alert fatigue — terlalu banyak low-priority alertsDispatcher mulai ignore alertsHigh
5Belum ada data retention policy otomatis di InfluxDBStorage cost terus naikLow

🎉 Conclusion

Bekerja dalam pengembangan Minerva Digital Platform adalah tentang menghadapi tantangan fisik dunia nyata — koneksi buruk, debu, cuaca, hardware yang rentan gagal — menggunakan solusi di ranah cyber: edge computing, event-driven architecture, in-memory processing, dan predictive analytics.

Ini membuktikan bahwa resilient backend engineering adalah tulang punggung dari digitalisasi industri alat berat. Tantangannya bukan hanya menulis kode yang berjalan, tapi kode yang tetap berjalan di tengah kondisi yang membuat kebanyakan sistem menyerah.

Engineering Philosophy

"The best mining technology is the one miners forget is there."

Operator dump truck tidak perlu tahu tentang Kafka, Bloom filter, atau Ray-Casting algorithm. Yang mereka tahu: layar di dashboard truk mereka menunjukkan rute terbaik, peringatan datang sebelum masalah muncul, dan mereka bisa pulang dengan selamat setiap hari. Itulah definisi engineering yang berhasil.


Related Articles

Case Study: Minerva Digital Platform — Petrosea | Faisal Affan