Membangun Platform Exchange Crypto Enterprise: Business Logic, Fitur, dan Domain Knowledge

Bagaimana saya membangun hub-and-spoke platform untuk 3 site tambang di Kalimantan — fleet management real-time, HSE monitoring otomatis, dan mine planning analytics yang langsung mendeteksi pencurian BBM di bulan pertama deployment.

"To be the first class total mining services solution — through technology adoption and operational excellence." — Visi PT Harmoni Panca Utama
PT Harmoni Panca Utama (HPU) adalah salah satu perusahaan mining services terbesar di Indonesia, beroperasi di multi-site Kalimantan (Timur, Tengah, dan Utara) dengan 1,000-5,000+ karyawan dan armada alat berat yang masif. Saya berkontribusi dalam perancangan dan pengembangan digital platform yang mengintegrasikan Fleet Management, HSE monitoring, dan mine operations analytics untuk mendukung visi Operational Excellence HPU.
Proyek ini mentransformasi operasional tambang HPU dari proses yang sebagian besar manual — pencatatan kertas, komunikasi radio, spreadsheet Excel — menjadi platform digital terintegrasi yang memberikan visibility end-to-end ke seluruh operasi lintas site.
Artikel ini membedah tantangan teknis, arsitektur, dan keputusan engineering di balik pembangunan platform ini.
PT Harmoni Panca Utama menjalankan operasi tambang batu bara di beberapa site strategis di Kalimantan:
Operasi tersebar di Kutai Kartanegara (Kaltim), Barito Utara (Kalteng), dan Bulungan (Kaltara) — masing-masing dengan kondisi geografis dan tantangan konektivitas berbeda.
Ratusan unit alat berat — dump truck, excavator, bulldozer, grader — beroperasi 24/7 dengan shift system, membutuhkan koordinasi real-time.
Standar keselamatan HPU sangat tinggi, dibuktikan dengan pencapaian jutaan jam kerja tanpa kecelakaan. Sistem digital harus mendukung dan memperkuat budaya safety ini.
HPU menjalankan proses green mining — aspek lingkungan harus terintegrasi dalam monitoring operasional, termasuk tracking reklamasi dan emisi.
| Aspek | Kondisi Lama | Dampak |
|---|---|---|
| Fleet Tracking | Radio HT + pengawas visual | Dispatcher tidak tahu posisi semua unit secara real-time |
| Cycle Time | Catat manual di kertas oleh checker | Data terlambat masuk, sering tidak akurat, sulit dianalisis |
| HSE Reporting | Form kertas → input manual ke Excel | Incident report memakan waktu berjam-jam, analytics terbatas |
| Fuel Management | Catat manual di SPBU site | Estimasi konsumsi tidak akurat, rawan kebocoran/penyalahgunaan |
| Mine Planning | Software di PC kantor, tidak terintegrasi | Gap antara planning dan eksekusi di lapangan |
| Multi-Site Visibility | Laporan mingguan via email | Management di Jakarta tidak punya real-time visibility |
Estimasi konservatif: 8-12% potensi produktivitas hilang karena information lag — dispatcher mengalokasikan truk berdasarkan informasi yang sudah telat 10-15 menit, cycle time tercatat tidak akurat, dan keputusan maintenance bersifat reaktif (rusak baru perbaiki) bukan proaktif. Dalam skala operasi HPU, ini bernilai miliaran rupiah per bulan.
Platform dibangun dengan arsitektur Hub-and-Spoke — setiap site memiliki edge server lokal yang memproses data secara mandiri, kemudian disinkronisasi ke central hub di cloud.
graph TD
subgraph "Site Kaltim - Kutai Kartanegara"
A1[GPS Tracker Fleet] -->|MQTT| B1[Edge Server Kaltim]
A2[Fuel Sensor] -->|Modbus/TCP| B1
A3[Weighbridge] -->|API| B1
B1 -->|Local Dashboard| C1[Site Control Room]
end
subgraph "Site Kalteng - Barito Utara"
A4[GPS Tracker Fleet] -->|MQTT| B2[Edge Server Kalteng]
A5[Fuel Sensor] -->|Modbus/TCP| B2
B2 -->|Local Dashboard| C2[Site Control Room]
end
subgraph "Site Kaltara - Bulungan KMO"
A6[GPS Tracker Fleet] -->|MQTT| B3[Edge Server Kaltara]
A7[Fuel Sensor] -->|Modbus/TCP| B3
B3 -->|Local Dashboard| C3[Site Control Room]
end
B1 -->|Sync via HTTPS| D[Central Platform - Cloud]
B2 -->|Sync via HTTPS| D
B3 -->|Sync via HTTPS/VSAT| D
D --> E[Central Dashboard - Jakarta HQ]
D --> F[Analytics & BI Engine]
D --> G[HSE Management Module]
D --> H[Mine Planning Integration]
D --> I[(Central Database)]sequenceDiagram
participant F as Fleet (GPS/Sensor)
participant E as Edge Server (Site)
participant DB as Local DB
participant C as Central Cloud
participant D as Dashboard (HQ)
participant M as Management App
F->>E: Raw telemetry (MQTT, 5-sec interval)
E->>E: Normalize, validate, buffer
E->>DB: Store locally (always available)
E->>E: Process rules (geofence, speed, idle)
alt Koneksi cloud tersedia
E->>C: Batch sync (compressed, delta)
C->>C: Aggregate multi-site data
C->>D: Push real-time update (WebSocket)
C->>M: API for mobile app
else Koneksi putus
E->>DB: Continue buffering
Note over E,DB: Site tetap beroperasi normal
end
Note over E,C: Saat koneksi pulih
DB->>E: Read buffered data
E->>C: Bulk sync (catch-up)
C->>C: Deduplicate & mergeKey Architecture Decisions:
Awalnya dipertimbangkan arsitektur full cloud — semua data langsung dikirim ke cloud. Tapi konektivitas di site Kaltara (KMO Bulungan) hanya via VSAT 512 kbps shared. Arsitektur hub-and-spoke memastikan setiap site mandiri secara operasional meski koneksi ke cloud terputus total.
Alert kritis (geofence breach, overspeed, engine anomaly) diproses di edge server, bukan di cloud. Latency ke cloud bisa 1-3 detik, tapi alert safety harus sub-second. Edge server menjalankan rule engine lokal yang langsung push ke control room.
MQTT digunakan antara GPS tracker dan edge server karena lightweight dan handal di koneksi flaky. Tapi untuk edge-to-cloud sync, dipilih HTTPS karena lebih mudah di-manage melalui firewall dan proxy corporate, dan bandwidth sudah cukup karena data di-batch dan dikompresi.
Meski satu platform, setiap site memiliki data isolation yang ketat. Supervisor di Site Kaltim tidak bisa melihat data operasional Site Kaltara, kecuali memiliki role management yang sesuai. Ini penting untuk keamanan data kontrak klien yang berbeda di tiap site.
| Technology | Peran | Alasan |
|---|---|---|
| Golang | Core backend services, API gateway | High-concurrency, low memory footprint, cocok untuk handle ribuan koneksi telemetri |
| PostgreSQL | Primary database (central) | ACID compliance, PostGIS untuk spatial queries, mature ecosystem |
| Apache Kafka | Event streaming (central hub) | Durable event log, replay capability, multi-consumer support |
| Redis | Real-time state cache | Fleet current state, session management, rate limiting |
| gRPC | Inter-service communication | Low-latency, strongly-typed, efficient untuk internal services |
| Technology | Peran | Alasan |
|---|---|---|
| MQTT (Mosquitto) | Device-to-edge protocol | Lightweight, designed untuk low-bandwidth environments |
| SQLite (WAL mode) | Edge local database | Reliable, zero-config, tahan power fluctuation |
| Golang (edge binary) | Edge server runtime | Single binary deployment, no runtime dependencies |
| Protocol Buffers | Edge-to-cloud serialization | 5-8x lebih kecil dari JSON, hemat bandwidth VSAT |
| Modbus TCP | Fuel sensor protocol | Standard industri untuk sensor industrial |
| Technology | Peran | Alasan |
|---|---|---|
| InfluxDB | Time-series data (telemetri) | Query temporal cepat, kompresi efisien |
| Apache Superset | BI & analytics dashboard | Open-source, SQL-based, customizable charts |
| PostGIS | Geospatial queries | Geofence management, haul road mapping, area calculation |
| Python (pandas + scikit-learn) | Predictive analytics | Fuel consumption forecasting, maintenance prediction |
| Technology | Peran | Alasan |
|---|---|---|
| React | Web dashboard (control room + HQ) | Component-based, rich ecosystem untuk maps dan charts |
| React Native | Mobile app (supervisor) | Cross-platform, offline-capable dengan local storage |
| Mapbox GL JS | Real-time fleet map | High-performance rendering ratusan marker bergerak |
| WebSocket | Real-time dashboard updates | Push-based updates tanpa polling |
| Recharts + D3.js | Data visualization | Cycle time charts, fuel trends, production analytics |
| Property | Type | Description |
|---|---|---|
| device_id | string | Unique identifier GPS tracker (format: SITE-TYPE-NNN, e.g. KTM-DT-042 = Kaltim Dump Truck #42). |
| unit_code | string | Kode unit alat berat sesuai register HPU (e.g. HD785-007). |
| timestamp | time.Time | UTC timestamp saat data di-capture oleh GPS tracker. |
| latitude | float64 | Koordinat GPS latitude (WGS84). |
| longitude | float64 | Koordinat GPS longitude (WGS84). |
| speed | float64 | Kecepatan kendaraan dalam km/h. |
| heading | float64 | Arah hadap kendaraan dalam derajat (0-360). |
| ignition | boolean | Status ignition on/off — krusial untuk menghitung idle time vs productive time. |
| fuel_level | float64 | Level bahan bakar dalam liter (dari fuel sensor analog). |
| payload_ton | float64 | Berat muatan dump truck dalam ton (dari payload monitoring system atau weighbridge). |
| engine_hours | float64 | Total jam operasional mesin (hour meter) — basis untuk jadwal maintenance. |
| site_id | string | Identifier site: 'KTM' (Kaltim), 'KTG' (Kalteng), 'KTR' (Kaltara). |
Problem: Dispatcher di control room mengandalkan radio HT untuk mengarahkan armada. Dengan puluhan unit dump truck dan beberapa excavator beroperasi simultan, dispatcher tidak memiliki gambaran real-time posisi dan status setiap unit. Akibatnya, truk sering mengantre di satu loading point sementara loading point lain idle.
Solution: Fleet Management System real-time dengan dispatch optimization berbasis posisi, antrean, dan cycle time historis.
graph TD
A[GPS Tracker di Setiap Unit] -->|MQTT 5-sec| B[Edge Server]
B -->|Process| C[Fleet State Manager]
C -->|Update| D[(Redis - Current State)]
D -->|Feed| E[Dispatch Optimizer]
F[Queue Monitor per Loading Point] -->|Feed| E
G[Cycle Time History - InfluxDB] -->|Feed| E
E --> H{Assignment Decision}
H -->|Shortest Queue| I[Assign ke Excavator A]
H -->|Nearest Unit| J[Assign ke Excavator B]
H -->|Balanced Load| K[Assign ke Excavator C]
I --> L[Push ke Driver Display / Radio]
J --> L
K --> L
L --> M[Driver Follow Route]
M --> N[Arrival Detected via Geofence]
N --> O[Update Queue Counter]
style E fill:#dbeafe,stroke:#3b82f6,color:#000
style H fill:#fef3c7,stroke:#f59e0b,color:#000// Multi-Factor Dispatch Optimizer
type DispatchDecision struct {
TruckID string `json:"truck_id"`
UnitCode string `json:"unit_code"`
ExcavatorID string `json:"excavator_id"`
LoadingPoint string `json:"loading_point"`
EstimatedETA float64 `json:"eta_minutes"`
QueuePosition int `json:"queue_position"`
Score float64 `json:"score"`
}
func (d *DispatchService) OptimizeAssignment(ctx context.Context, truckID string) (*DispatchDecision, error) {
// 1. Get current truck state from Redis
truck, err := d.cache.GetFleetState(ctx, truckID)
if err != nil {
return nil, fmt.Errorf("truck state not found: %w", err)
}
// 2. Get all active excavators and their queue lengths
excavators, err := d.getActiveExcavators(ctx, truck.SiteID)
if err != nil {
return nil, err
}
// 3. Score each excavator: distance (40%) + queue (35%) + avg cycle (25%)
var best *DispatchDecision
var bestScore float64
for _, exc := range excavators {
distance := haversineKm(truck.Location, exc.Location)
avgCycle := d.getCycleTimeAvg(ctx, truckID, exc.ID)
distScore := 1.0 / (1.0 + distance)
queueScore := 1.0 / (1.0 + float64(exc.QueueLength))
cycleScore := 1.0 / (1.0 + avgCycle)
score := (distScore * 0.40) + (queueScore * 0.35) + (cycleScore * 0.25)
if score > bestScore {
bestScore = score
best = &DispatchDecision{
TruckID: truckID,
UnitCode: truck.UnitCode,
ExcavatorID: exc.ID,
LoadingPoint: exc.LoadingPoint,
EstimatedETA: distance / avgSpeedKmPerMin(truck.UnitType),
QueuePosition: exc.QueueLength + 1,
Score: score,
}
}
}
// 4. Publish dispatch decision
d.kafka.Publish("dispatch.decision", best)
return best, nil
}Cycle Time Tracking:
graph LR
A["🟢 LOADING<br/>Excavator mengisi muatan"] --> B["🔵 HAULING (Loaded)<br/>Truk menuju dumping area"]
B --> C["🟡 DUMPING<br/>Menurunkan muatan"]
C --> D["🔵 HAULING (Empty)<br/>Kembali ke loading"]
D --> E["🟠 QUEUING<br/>Menunggu giliran loading"]
E --> A
style A fill:#d1fae5,stroke:#10b981,color:#000
style B fill:#dbeafe,stroke:#3b82f6,color:#000
style C fill:#fef3c7,stroke:#f59e0b,color:#000
style D fill:#dbeafe,stroke:#3b82f6,color:#000
style E fill:#fed7aa,stroke:#f97316,color:#000Setiap fase cycle time terdeteksi otomatis via geofence (masuk/keluar loading area, dumping area) dan ignition status (moving vs stationary). Data ini digunakan untuk menghitung:
Results:
| Metric | Sebelum (Manual) | Sesudah (FMS) | Improvement |
|---|---|---|---|
| Avg cycle time | 35 menit | 26 menit | ↓ 26% |
| Queue time at loading | 10-15 menit | 3-5 menit | ↓ 65% |
| Truck utilization (PA × MA × UA) | 65% | 79% | ↑ 22% |
| Dispatch decision time | 2-3 menit (radio) | < 5 detik (auto) | ↓ 97% |
| Cycle time data accuracy | ~60% (manual) | 99%+ (auto) | ↑ 65% |
Problem: Bahan bakar adalah biaya operasional terbesar kedua setelah sewa alat (bisa mencapai 25-30% total opex). Pencatatan manual di SPBU site rawan ketidakakuratan, dan deteksi fuel theft atau abnormal consumption hampir mustahil dilakukan secara real-time.
Solution: End-to-end fuel tracking dari tangki penyimpanan, proses pengisian, hingga konsumsi per unit — dengan anomaly detection otomatis.
graph TD
A[Fuel Storage Tank] -->|Level Sensor| B[Edge Server]
C[Fuel Dispenser / SPBU Site] -->|Flow Meter| B
D[Vehicle Fuel Sensor] -->|MQTT| B
B --> E[Fuel Reconciliation Engine]
E --> F{Anomaly Check}
F -->|Normal| G[Log & Dashboard]
F -->|Sudden Drop > Threshold| H[Alert: Possible Theft]
F -->|Consumption > 130% Baseline| I[Alert: Inefficient Unit]
F -->|Refuel vs Dispense Mismatch| J[Alert: Reconciliation Gap]
H --> K[Notify Security & Supervisor]
I --> L[Notify Maintenance Team]
J --> M[Notify Fuel Coordinator]
style F fill:#fef3c7,stroke:#f59e0b,color:#000
style H fill:#fee2e2,stroke:#ef4444,color:#000
style I fill:#fef3c7,stroke:#f59e0b,color:#000
style J fill:#fef3c7,stroke:#f59e0b,color:#000// Fuel Anomaly Detection Rules
type FuelAnomaly struct {
UnitCode string `json:"unit_code"`
Type string `json:"type"`
Severity string `json:"severity"`
Description string `json:"description"`
DetectedAt time.Time `json:"detected_at"`
FuelDelta float64 `json:"fuel_delta_liters"`
Expected float64 `json:"expected_liters"`
}
func (d *AnomalyDetector) Evaluate(ctx context.Context, event FuelEvent) []FuelAnomaly {
var anomalies []FuelAnomaly
unit := d.getUnitProfile(ctx, event.UnitCode)
switch event.Type {
case "consumption":
// Rule 1: Consumption rate vs baseline
baseline := d.getBaselineConsumption(ctx, event.UnitCode, event.ActivityType)
ratio := event.ConsumptionRate / baseline.AvgRate
if ratio > 1.30 {
anomalies = append(anomalies, FuelAnomaly{
UnitCode: event.UnitCode,
Type: "HIGH_CONSUMPTION",
Severity: "WARNING",
Description: fmt.Sprintf("%s konsumsi %.1f L/jam vs baseline %.1f L/jam (%.0f%% di atas normal)",
event.UnitCode, event.ConsumptionRate, baseline.AvgRate, (ratio-1)*100),
FuelDelta: event.ConsumptionRate - baseline.AvgRate,
Expected: baseline.AvgRate,
})
}
case "level_change":
// Rule 2: Sudden fuel level drop (potential theft)
if event.FuelDelta < -50 && !d.hasRecentRefuelRecord(ctx, event.UnitCode) {
anomalies = append(anomalies, FuelAnomaly{
UnitCode: event.UnitCode,
Type: "SUDDEN_DROP",
Severity: "CRITICAL",
Description: fmt.Sprintf("%s fuel level turun %.0f liter tanpa record refueling — investigasi diperlukan",
event.UnitCode, -event.FuelDelta),
FuelDelta: event.FuelDelta,
})
}
case "refueling":
// Rule 3: Refuel amount vs dispenser record mismatch
dispenserRecord := d.getDispenserRecord(ctx, event.UnitCode, event.Timestamp)
if dispenserRecord != nil {
gap := math.Abs(event.FuelDelta - dispenserRecord.Volume)
tolerance := dispenserRecord.Volume * 0.05 // 5% tolerance
if gap > tolerance {
anomalies = append(anomalies, FuelAnomaly{
UnitCode: event.UnitCode,
Type: "RECONCILIATION_GAP",
Severity: "WARNING",
Description: fmt.Sprintf("%s selisih refuel: sensor %.0f L vs dispenser %.0f L (gap: %.0f L)",
event.UnitCode, event.FuelDelta, dispenserRecord.Volume, gap),
FuelDelta: gap,
Expected: dispenserRecord.Volume,
})
}
}
}
return anomalies
}Dalam bulan pertama deployment, sistem mendeteksi 3 insiden sudden fuel drop di malam hari pada unit yang seharusnya parkir — total ~650 liter solar (~Rp 10 juta). Investigasi mengkonfirmasi fuel theft. Sebelum sistem ini, anomali semacam ini tidak pernah terdeteksi karena pencatatan manual hanya dilakukan per shift (setiap 12 jam).
Results:
| Metric | Sebelum (Manual) | Sesudah (Digital) | Improvement |
|---|---|---|---|
| Fuel tracking accuracy | ~85% (manual log) | 98%+ (sensor) | ↑ 15% |
| Fuel anomaly detection time | 1-7 hari (audit) | < 5 menit (real-time) | ↓ 99%+ |
| Fuel cost savings | Baseline | ↓ 8-12% per tahun | Significant |
| Reconciliation gap | ~5-8% | < 1% | ↓ 87% |
Problem: HPU memiliki standar HSE yang sangat tinggi (dibuktikan dengan pencapaian 5.7+ juta jam kerja tanpa kecelakaan di salah satu site). Namun, proses pencatatan HSE masih berbasis form kertas — near miss reporting lambat, safety inspection sulit di-track, dan analytics untuk trend identification terbatas.
Solution: HSE management module terintegrasi dalam platform — dari incident reporting, safety inspection, hingga predictive safety analytics.
graph TD
A["📱 Mobile App<br/>(Supervisor/Operator)"] --> B{HSE Module}
B --> C["🚨 Incident Report<br/>Near Miss / Accident"]
B --> D["📋 Safety Inspection<br/>Daily / Weekly Checklist"]
B --> E["🎓 Safety Training<br/>Log & Certification"]
B --> F["⚡ SWA / JSA<br/>Safe Work Analysis"]
C --> G[HSE Dashboard - Site Level]
D --> G
E --> G
F --> G
G --> H[Trend Analytics Engine]
H --> I{Pattern Detection}
I -->|High near-miss zone| J["🟡 Warning: Hotspot Area"]
I -->|Equipment type trend| K["🟡 Warning: Equipment Risk"]
I -->|Time pattern trend| L["🟡 Warning: Shift Fatigue"]
J --> M[Safety Bulletin Board]
K --> M
L --> M
G --> N[HSE Summary - HQ Jakarta]
style C fill:#fee2e2,stroke:#ef4444,color:#000
style J fill:#fef3c7,stroke:#f59e0b,color:#000
style K fill:#fef3c7,stroke:#f59e0b,color:#000
style L fill:#fef3c7,stroke:#f59e0b,color:#000HSE Reporting Flow:
Supervisor atau operator membuka HSE module di aplikasi mobile. Form incident report dirancang offline-first — bisa diisi lengkap tanpa koneksi internet, termasuk foto dan koordinat GPS otomatis.
Form menggunakan guided flow berdasarkan tipe insiden (near miss, first aid, medical treatment, lost time injury). Setiap tipe memiliki field mandatory yang berbeda, mengurangi incomplete report dari ~25% menjadi < 3%.
Berdasarkan severity dan tipe insiden, sistem otomatis me-route report ke pihak yang tepat: HSE Officer, Site Manager, atau langsung ke HQ jika severity critical. Tidak perlu lagi forward email manual.
HSE team melakukan investigasi menggunakan template 5-Why Analysis atau Fishbone Diagram yang sudah tersedia di sistem. Temuan dan corrective action tercatat dan trackable.
Setiap corrective action memiliki PIC, deadline, dan status tracking. Sistem mengirim reminder otomatis jika mendekati atau melewati deadline. Management bisa monitor completion rate secara real-time.
Data historis dianalisis untuk mengidentifikasi pattern — area mana yang sering terjadi near miss, jam berapa insiden paling sering terjadi, tipe equipment apa yang paling berisiko. Ini mengubah HSE dari reaktif (investigasi setelah kejadian) menjadi proaktif (cegah sebelum terjadi).
Results:
| Metric | Sebelum (Paper-based) | Sesudah (Digital) | Improvement |
|---|---|---|---|
| Near miss reporting time | 24-48 jam | < 30 menit | ↓ 96% |
| Incomplete HSE report | ~25% | < 3% | ↓ 88% |
| Corrective action completion rate | ~65% | 92% | ↑ 42% |
| Time to generate HSE report (monthly) | 3-5 hari | Real-time (auto) | ↓ 100% |
| Safety trend identification | Manual (retrospective) | Proactive (pattern-based) | Paradigm shift |
Problem: Manajemen HPU di Jakarta Head Office tidak memiliki real-time visibility ke operasi di seluruh site. Laporan datang mingguan via email dalam format spreadsheet — sudah stale saat dibaca. Perbandingan performa antar-site sulit dilakukan secara apples-to-apples.
Solution: Centralized command center dashboard yang menampilkan KPI operasional seluruh site dalam satu layar, terupdate secara real-time.
Real-time fleet map menampilkan posisi semua unit di semua site pada satu peta. Filter by site, equipment type, atau status (active/idle/maintenance).
| Widget | Data | Update Interval |
|---|---|---|
| Live Fleet Map | Posisi semua unit (multi-site) | 5 detik |
| Active Units Count | Unit beroperasi vs total fleet | 30 detik |
| Idle Alert | Unit idle > 15 menit | Real-time |
| Equipment Availability | PA / MA / UA per site | 5 menit |
| Dispatch Queue | Antrean per loading point | Real-time |
Production dashboard menampilkan target vs actual, cycle time analytics, dan trend harian/mingguan/bulanan.
| Widget | Data | Update Interval |
|---|---|---|
| Daily Production (Ton) | Target vs actual per site | 15 menit |
| Cycle Time Trend | Avg cycle time per shift | Per cycle |
| Overburden Removal (BCM) | Volume OB per site | 1 jam |
| Strip Ratio | Aktual vs plan | 1 jam |
| Productivity per Unit | Ton/jam per equipment | Per trip |
Safety dashboard menampilkan HSE statistics, incident map, dan leading indicators.
| Widget | Data | Update Interval |
|---|---|---|
| Safe Man Hours | Jam kerja tanpa kecelakaan | Real-time |
| Incident Heatmap | Lokasi insiden di peta site | Daily |
| Near Miss Trend | Jumlah near miss per minggu | Daily |
| Corrective Action Status | Open / In-Progress / Closed | Real-time |
| Safety Inspection Score | Compliance % per area | Weekly |
Fuel dashboard menampilkan konsumsi, stok, dan anomali per site dan per unit.
| Widget | Data | Update Interval |
|---|---|---|
| Daily Fuel Consumption | Liter per site | 1 jam |
| Fuel Cost per Ton | Biaya BBM per ton material | Daily |
| Tank Level | Stok tangki penyimpanan | 30 menit |
| Top Consumers | Unit dengan konsumsi tertinggi | Daily |
| Anomaly Alerts | Deteksi anomali aktif | Real-time |
Key Technical Implementation:
// Multi-Site KPI Aggregator — runs every 5 minutes
type SiteKPI struct {
SiteID string `json:"site_id"`
SiteName string `json:"site_name"`
ActiveUnits int `json:"active_units"`
TotalUnits int `json:"total_units"`
AvgCycleTime float64 `json:"avg_cycle_time_min"`
ProductionTons float64 `json:"production_tons_today"`
TargetTons float64 `json:"target_tons_today"`
FuelConsumption float64 `json:"fuel_liters_today"`
SafeManHours int64 `json:"safe_man_hours"`
OpenIncidents int `json:"open_incidents"`
TruckUtilization float64 `json:"truck_utilization_pct"`
}
func (a *Aggregator) ComputeMultiSiteKPI(ctx context.Context) ([]SiteKPI, error) {
sites := []string{"KTM", "KTG", "KTR"}
var results []SiteKPI
for _, siteID := range sites {
// Parallel fetch from different data sources
var wg sync.WaitGroup
var fleet FleetSummary
var production ProductionSummary
var fuel FuelSummary
var hse HSESummary
wg.Add(4)
go func() { defer wg.Done(); fleet = a.fleetService.GetSiteSummary(ctx, siteID) }()
go func() { defer wg.Done(); production = a.productionService.GetDailySummary(ctx, siteID) }()
go func() { defer wg.Done(); fuel = a.fuelService.GetDailySummary(ctx, siteID) }()
go func() { defer wg.Done(); hse = a.hseService.GetSiteSummary(ctx, siteID) }()
wg.Wait()
results = append(results, SiteKPI{
SiteID: siteID,
SiteName: getSiteName(siteID),
ActiveUnits: fleet.ActiveCount,
TotalUnits: fleet.TotalCount,
AvgCycleTime: fleet.AvgCycleTime,
ProductionTons: production.ActualTons,
TargetTons: production.TargetTons,
FuelConsumption: fuel.TotalLiters,
SafeManHours: hse.SafeManHours,
OpenIncidents: hse.OpenIncidents,
TruckUtilization: fleet.Utilization,
})
}
// Cache aggregated KPI
a.cache.Set(ctx, "kpi:multi_site", results, 5*time.Minute)
// Push to connected dashboards via WebSocket
a.wsHub.Broadcast("kpi.update", results)
return results, nil
}Sebelumnya, manajemen di Jakarta baru mendapat laporan operasional setiap Senin pagi (untuk minggu sebelumnya) dalam format Excel yang dikirim via email. Sekarang, mereka membuka dashboard dan melihat seluruh operasi multi-site secara real-time. Keputusan yang dulunya membutuhkan waktu berhari-hari (misal: realokasi alat antar-site) sekarang bisa diambil dalam hitungan jam.
Problem: Konektivitas di setiap site berbeda drastis — Site Kaltim memiliki 4G yang cukup stabil, Site Kalteng via radio link, dan Site Kaltara (KMO Bulungan) hanya mengandalkan VSAT 512 kbps shared. Platform harus tetap berjalan normal di semua kondisi.
Solution: Adaptive sync pipeline dengan prioritization dan compression yang disesuaikan per bandwidth tier.
graph TD
A[Edge Server] --> B{Bandwidth Detection}
B -->|"4G / Fiber (> 5 Mbps)"| C[Full Sync Mode]
B -->|"Radio Link (1-5 Mbps)"| D[Optimized Sync Mode]
B -->|"VSAT (< 1 Mbps)"| E[Minimal Sync Mode]
B -->|"No Connection"| F[Buffer Mode]
C --> G["Sync semua data, 5-sec interval<br/>JSON + gzip"]
D --> H["Sync essential data, 30-sec interval<br/>Protobuf + zstd"]
E --> I["Sync critical only, 5-min interval<br/>Protobuf + zstd + delta"]
F --> J["Buffer ke SQLite<br/>Sync saat tersedia"]
G --> K[Central Cloud]
H --> K
I --> K
J -.->|"Saat koneksi pulih"| K
style C fill:#d1fae5,stroke:#10b981,color:#000
style D fill:#fef3c7,stroke:#f59e0b,color:#000
style E fill:#fed7aa,stroke:#f97316,color:#000
style F fill:#fee2e2,stroke:#ef4444,color:#000// Adaptive Sync Manager — adjusts behavior based on bandwidth
type SyncMode string
const (
SyncFull SyncMode = "full" // > 5 Mbps
SyncOptimized SyncMode = "optimized" // 1-5 Mbps
SyncMinimal SyncMode = "minimal" // < 1 Mbps
SyncBuffer SyncMode = "buffer" // No connection
)
func (s *SyncManager) detectMode(ctx context.Context) SyncMode {
bandwidth := s.bandwidthProbe.Measure(ctx) // Periodic bandwidth test
switch {
case bandwidth == 0:
return SyncBuffer
case bandwidth < 1_000_000: // < 1 Mbps
return SyncMinimal
case bandwidth < 5_000_000: // < 5 Mbps
return SyncOptimized
default:
return SyncFull
}
}
func (s *SyncManager) SyncLoop(ctx context.Context) {
for {
mode := s.detectMode(ctx)
switch mode {
case SyncFull:
s.syncAll(ctx, 5*time.Second, EncodingJSON, CompressionGzip)
case SyncOptimized:
s.syncEssential(ctx, 30*time.Second, EncodingProtobuf, CompressionZstd)
case SyncMinimal:
s.syncCriticalOnly(ctx, 5*time.Minute, EncodingProtobuf, CompressionZstd)
case SyncBuffer:
time.Sleep(10 * time.Second) // Check again in 10s
continue
}
}
}
// Priority: alerts > fleet state > fuel > production > HSE (non-urgent)
func (s *SyncManager) syncCriticalOnly(ctx context.Context, interval time.Duration, enc Encoding, comp Compression) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for range ticker.C {
// Only sync: safety alerts + fleet current position + fuel anomalies
alerts := s.buffer.GetUnsynced(ctx, PriorityCritical, 50)
if len(alerts) > 0 {
payload := encode(alerts, enc, comp)
s.httpClient.Post(ctx, "/api/v1/sync/critical", payload)
s.buffer.MarkSynced(ctx, alerts)
}
}
}Bandwidth Usage per Sync Mode:
| Sync Mode | Interval | Avg Payload/sync | Monthly Est. | Use Case |
|---|---|---|---|---|
| Full | 5 sec | 12 KB | ~6 GB | Site dengan fiber/4G |
| Optimized | 30 sec | 3 KB (Protobuf) | ~260 MB | Site dengan radio link |
| Minimal | 5 min | 800 bytes | ~7 MB | Site VSAT (Kaltara KMO) |
| Buffer | — | 0 (local only) | 0 | Koneksi putus total |
Di Site KMO Bulungan, bandwidth VSAT 512 kbps di-share oleh seluruh camp — office, mess, dan operasional. Platform hanya mendapat alokasi ~100 kbps. Dengan mode Minimal Sync menggunakan Protocol Buffers + Zstd compression, kami berhasil mengirim data fleet tracking untuk 40+ unit hanya dengan ~7 MB per bulan. Tanpa optimasi ini, JSON mentah akan membutuhkan ~50 MB — mustahil di bandwidth tersebut.
Site terbesar HPU dengan konektivitas terbaik dipilih sebagai pilot. Deploy edge server, pasang GPS tracker di 60 unit, dan launch Fleet Management + basic dashboard. Validasi data accuracy dan user acceptance selama 2 bulan.
Setelah fleet tracking stabil, integrasikan fuel sensor dan flow meter di SPBU site. Deploy anomaly detection rules. Kalibrasi threshold per tipe equipment karena konsumsi HD785 (40-ton) berbeda dengan PC2000 (excavator).
Launch HSE reporting via mobile app untuk supervisor. Migrasi data historis insiden dari Excel ke platform. Training seluruh tim HSE di site untuk menggunakan digital workflow.
Deploy edge server dan GPS tracker. Adaptasi untuk koneksi radio link (optimized sync mode). Penyesuaian mine plan area dan geofence sesuai topografi site.
Site paling menantang — VSAT only. Deploy dengan minimal sync mode. Validasi bahwa operasional tetap berjalan normal saat koneksi terputus. Edge server harus 100% reliable secara mandiri.
Launch multi-site dashboard di kantor pusat. Training untuk management team. Setup automated daily/weekly report generation menggantikan spreadsheet manual.
| # | Module | Before | After | Estimated Annual Value |
|---|---|---|---|---|
| 1 | Fleet Management | 35 min cycle, 65% utilization | 26 min, 79% | +15-20% production volume |
| 2 | Fuel Management | 5-8% reconciliation gap | < 1% gap + theft detection | Rp 2-4 miliar/tahun savings |
| 3 | HSE Digital | Paper-based, reactive | Digital, proactive | Priceless (nyawa manusia) + compliance |
| 4 | Multi-Site Dashboard | Weekly email reports | Real-time command center | Faster decision making → revenue impact |
| 5 | Edge-to-Cloud Sync | N/A (no remote site support) | Works on 100 kbps VSAT | Enabler untuk operasi di remote site |
Tantangan terbesar post-launch adalah hardware reliability:
| Problem | Penyebab | Frekuensi | Solusi |
|---|---|---|---|
| GPS drift | Debu tebal menutupi antena | ~5% unit/hari | Auto-detect drift > 50m dalam 1 detik → filter out |
| Tracker offline | Getaran melepas konektor | ~3% unit/hari | Industrial connector + vibration-dampened mounting |
| Fuel sensor noise | Getaran di jalan tambang | Konstan | Moving average filter (window 30 detik) |
| Power spike | Engine start/stop | Setiap ignition | Wide-voltage regulator (9-36V DC) + surge protector |
Dispatcher yang sudah 10+ tahun menggunakan radio HT awalnya menolak transisi ke digital. Pendekatan yang berhasil: bukan mengganti radio, tapi menambah layer informasi. Radio tetap digunakan untuk komunikasi suara, tapi dispatch assignment didukung oleh data real-time di layar. Setelah 2-3 minggu, dispatcher sendiri yang enggan kembali ke cara lama karena informasi di dashboard jauh lebih kaya.
Supervisor lapangan terbiasa dengan clipboard dan form kertas. Beberapa memiliki keterbatasan dalam menggunakan smartphone. Solusi: form input yang sangat sederhana, tombol besar, minimal typing (banyak dropdown dan radio button), dan dukungan foto untuk menggantikan deskripsi tertulis panjang.
Saat dashboard pertama kali live, management terkejut melihat angka idle time yang tinggi dan cycle time yang lebih lambat dari yang selama ini dilaporkan manual. Ini bukan karena performa menurun — tapi karena data manual selama ini tidak akurat. Butuh beberapa session untuk menjelaskan bahwa data digital yang "terlihat buruk" justru merupakan realita yang selama ini tertutup oleh estimasi manual yang optimistis.
Tantangan unik: operator ganti shift setiap 12 jam, tapi GPS tracker tetap terpasang di unit. Sistem harus mengaitkan data telemetri dengan operator yang sedang bertugas, bukan hanya dengan unit. Solusi: NFC card tap saat shift start/end di kabin kendaraan untuk registrasi operator aktif.
Konektivitas terus membaik seiring investasi infrastruktur:
graph LR
A["2024 Q1<br/>🔴 VSAT Only<br/>512 kbps"] --> B["2024 Q3<br/>🟡 VSAT + Radio Link<br/>2 Mbps"]
B --> C["2025 Q1<br/>🟢 4G Tower<br/>10+ Mbps"]
C --> D["2025 Q3<br/>🟢 Fiber Backhaul<br/>50+ Mbps"]
style A fill:#fee2e2,stroke:#ef4444,color:#000
style B fill:#fef3c7,stroke:#f59e0b,color:#000
style C fill:#d1fae5,stroke:#10b981,color:#000
style D fill:#d1fae5,stroke:#10b981,color:#000Arsitektur adaptive sync yang kami bangun sejak awal terbukti future-proof — saat bandwidth meningkat, platform otomatis memanfaatkan kapasitas tambahan tanpa perubahan kode. Site yang dulunya hanya bisa minimal sync sekarang bisa full sync dengan latency minimal.
| # | Debt | Impact | Priority |
|---|---|---|---|
| 1 | Predictive maintenance model belum terintegrasi | Maintenance masih time-based, belum condition-based | High |
| 2 | Mine planning integration masih one-way | Plan di-push ke platform, tapi aktual belum auto-feedback ke planner | High |
| 3 | Dashboard belum support dark mode untuk control room malam | Operator night shift complain silau | Medium |
| 4 | Alert fatigue — terlalu banyak low-priority alerts | Supervisor mulai ignore non-critical alerts | High |
| 5 | Data retention policy belum otomatis di InfluxDB | Storage cost naik linear, perlu tiered storage | Medium |
| 6 | Mobile app belum fully offline-capable | HSE report di area jauh dari camp harus buffer manual | Medium |
Membangun platform digital untuk operasi tambang HPU adalah tentang memecahkan masalah di dua dunia yang bertabrakan — dunia digital yang menuntut konektivitas dan data real-time, versus dunia tambang yang beroperasi di pedalaman Kalimantan dengan debu, getaran, dan sinyal yang kadang ada, kadang tidak.
Keberhasilan platform ini bukan diukur dari secanggih apa teknologinya, tapi dari seberapa transparan teknologi itu bagi penggunanya. Dispatcher tetap fokus pada dispatching, bukan pada debugging. HSE officer tetap fokus pada keselamatan, bukan pada troubleshooting app.
HPU memiliki core values: Integrity, Hard Work, Smart Work, Complete Work, and Sincerity. Platform digital ini adalah manifestasi Smart Work — bukan menggantikan kerja keras tim tambang, tapi memperkuat mereka dengan informasi yang tepat, di waktu yang tepat, agar keputusan yang diambil semakin presisi.
Pada akhirnya, setiap baris kode yang ditulis untuk platform ini memiliki satu tujuan: membantu setiap pekerja tambang HPU pulang dengan selamat dan produktif setiap hari.
Case study serupa — membangun Minerva Digital Platform untuk fleet management dan predictive maintenance di Petrosea.
Arsitektur offline-first yang serupa — diaplikasikan untuk pembiayaan mikro syariah di area terpencil Indonesia.
Bagaimana transisi dari builder individu ke system thinker — pelajaran dari project-project berskala besar.



