Case Study: Minerva Digital Platform — Petrosea

- Engineering Case Study: Petrosea Minerva
- 🏔️ 1. The Challenge
- Kondisi Lapangan Tambang
- Tantangan Engineering
- 🏗️ 2. Architecture Overview
- High-Level Architecture
- Data Flow: Edge to Cloud
- 🛠️ 3. Technology Stack
- Telemetry Payload Schema
- 📁 4. Project Structure
- 🔍 5. Feature Deep Dives
- Feature #1: Telemetry Ingestion & Deduplication
- Feature #2: Dynamic Geofencing & Alerting
- Feature #3: Real-Time Fleet Dispatch & Cycle Time Optimization
- Feature #4: Predictive Maintenance Engine
- Feature #5: Edge-to-Cloud Sync Pipeline
- 🚢 6. Implementation & Deployment
- Phase 1: Edge Gateway Development
- Phase 2: Cloud Infrastructure Setup
- Phase 3: Rule Engine & Alerting
- Phase 4: Dashboard & Dispatch UI
- Phase 5: Predictive Maintenance Model
- Phase 6: Site Rollout & Calibration
- 🚀 7. Overall Business Impact
- ROI Summary
- 🏢 8. Post-Launch: Tantangan & Evolusi
- 8.1 Sensor Heterogeneity Nightmare
- 8.2 Konektivitas di Remote Site
- 8.3 Hardware Reliability di Lingkungan Tambang
- 🎓 9. Lessons Learned
- Technical Debt yang Masih Ada
- 🎉 Conclusion
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
- 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.
- Thundering Herd: Saat puluhan truk keluar dari blank spot secara bersamaan, semua device akan flood data historis ke server secara serentak.
- Heterogeneous Sensors: Sensor dari berbagai vendor (Caterpillar, Komatsu, Hitachi) mengirim data dalam format berbeda — harus dinormalisasi ke satu schema.
- Sub-Second Alerting: Geofence breach, overspeed, dan anomali mesin harus terdeteksi dalam hitungan milidetik, bukan detik.
- 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
| Technology | Peran | Alasan Dipilih |
|---|---|---|
| Golang | Core backend services | Goroutines mampu handle puluhan ribu koneksi TCP/MQTT concurrent dengan memory footprint minimal |
| Apache Kafka | Cloud-side message broker | High-throughput, durable, replayable event streaming |
| gRPC | Inter-service communication | Low-latency, strongly-typed, efficient binary serialization |
| Redis | Real-time state cache | Caching status terakhir kendaraan (lokasi, status, fuel) untuk dashboard real-time |
| Technology | Peran | Alasan Dipilih |
|---|---|---|
| MQTT (Mosquitto) | Edge protocol | Lightweight, designed untuk koneksi high latency / low bandwidth |
| SQLite (WAL mode) | Edge local buffer | Reliable offline storage, tahan sudden power loss |
| Embedded Linux | Gateway OS | Lightweight, customizable, long-term support |
| Protocol Buffers | Edge-to-cloud serialization | 3-10x lebih kecil dari JSON, hemat bandwidth |
| Technology | Peran | Alasan Dipilih |
|---|---|---|
| InfluxDB | Time-series database | Kompresi efisien untuk data telemetri, query temporal cepat |
| PostgreSQL | Master data & geofence | ACID compliance untuk data referensi, PostGIS untuk spatial queries |
| Apache Spark | Batch analytics | Processing historical data untuk reporting dan trend analysis |
| Python (scikit-learn) | Predictive maintenance ML | Model anomaly detection untuk engine health prediction |
| Technology | Peran | Alasan Dipilih |
|---|---|---|
| React | Dashboard web app | Component-based, rich ecosystem untuk data visualization |
| Mapbox GL | Real-time fleet map | High-performance WebGL map rendering untuk ratusan marker bergerak |
| WebSocket | Real-time updates | Push-based updates untuk dashboard tanpa polling |
| D3.js / Recharts | Analytics charts | Flexible data visualization untuk time-series dan aggregation |
Telemetry Payload Schema
Prop
Type
📁 4. Project Structure
🔍 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.
// 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:
// 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.
// 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.
// 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:
| Metric | Sebelum | Sesudah | Improvement |
|---|---|---|---|
| Duplicate data rate | ~18% (saat bulk sync) | < 0.01% | ↓ 99.9% |
| Ingestion throughput | ~5,000 events/sec | ~50,000 events/sec | ↑ 10x |
| Avg ingestion latency | 800ms | < 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.
// 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:
| Metric | Sebelum (Manual Radio) | Sesudah (Minerva) | Improvement |
|---|---|---|---|
| Geofence breach detection time | 5-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 rate | Baseline | ↓ 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.
// 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:
| Metric | Sebelum (Radio Dispatch) | Sesudah (Minerva) | Improvement |
|---|---|---|---|
| Avg cycle time | 32 menit | 24 menit | ↓ 25% |
| Queue time at loading area | 8-12 menit | 3-5 menit | ↓ 58% |
| Truck utilization rate | 68% | 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.
// 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:
| Metric | Sebelum (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 unit | Baseline | ↓ 22% | Significant |
| Mean Time To Repair (MTTR) | 8 jam | 4.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.
// 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:
| Metric | Sebelum | Sesudah | Improvement |
|---|---|---|---|
| Data loss during offline period | ~5% | 0% | ↓ 100% |
| Avg sync time (after reconnect) | 3-5 menit | < 30 detik | ↓ 90% |
| Bandwidth usage per sync | 4.2 MB | 890 KB (Protobuf) | ↓ 79% |
| Server overload during bulk sync | Frequent | Zero | ↓ 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
| # | Initiative | Before | After | Annual Value |
|---|---|---|---|---|
| 1 | Cycle time optimization | 32 min avg | 24 min avg | Rp 8.4M/truk/bulan (lebih banyak trip) |
| 2 | Predictive maintenance | 180 jam unplanned downtime | 65 jam | Rp 1.7 miliar/tahun (prevented failures) |
| 3 | Fuel efficiency | Baseline idle waste | -18% fuel | Rp 420 juta/tahun per site |
| 4 | Safety compliance | 45 overspeed/bulan | 12 overspeed/bulan | Priceless (nyawa manusia) |
🏢 8. Post-Launch: Tantangan & Evolusi
8.1 Sensor Heterogeneity Nightmare
Salah satu tantangan terbesar post-launch adalah keragaman sensor di armada:
| Vendor | Protocol | Data Format | Tantangan |
|---|---|---|---|
| Caterpillar | CAT Product Link | Proprietary JSON | Memerlukan adapter khusus + NDA untuk akses API |
| Komatsu | KOMTRAX | Binary via satellite | Latensi tinggi (15-30 detik), format berbeda per model |
| Hitachi | ConSite | REST API | Rate limit ketat, perlu credential management |
| Aftermarket GPS | MQTT / TCP | NMEA 0183 | Murah 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
| # | Debt | Impact | Priority |
|---|---|---|---|
| 1 | ML model retraining masih semi-manual | Model bisa stale jika pola operasi berubah | High |
| 2 | Belum ada circuit breaker di edge-to-cloud sync | Jika cloud down lama, buffer bisa penuh | Medium |
| 3 | Dashboard belum fully mobile-responsive | Dispatcher di lapangan harus pakai tablet | Medium |
| 4 | Alert fatigue — terlalu banyak low-priority alerts | Dispatcher mulai ignore alerts | High |
| 5 | Belum ada data retention policy otomatis di InfluxDB | Storage cost terus naik | Low |
🎉 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.