Masalah MOD dan Solusi IF-THEN-ELSE di Delta Horizon 16-bit
Pendekatan MOD sering dipakai untuk menghitung delta berbasis counter 16-bit di sistem DCS. Namun, metode ini rawan menghasilkan loncatan nilai saat terjadi anomali pembacaan.
Photo by Joshua Hoehne / Unsplash
Ditulis oleh Ketut Kumajaya | 08 Oktober 2025
Pengantar
Dalam sistem DCS berbasis counter 16-bit, perhitungan delta sering menghadapi masalah rollover—nilai kembali ke nol setelah mencapai batas 65.535. Jika tidak ditangani dengan benar, kondisi ini menghasilkan loncatan nilai besar (false delta) yang mengganggu akumulasi waktu maupun totalisasi.
Artikel ini membahas pendekatan IF-THEN-ELSE wrap-aware yang diimplementasikan dalam Function Block K_DELTA, serta hasil validasinya terhadap anomali delta akibat rollover.
Latar Belakang
Loncatan delta terdeteksi di pengujian lapangan setelah berhari-hari operasi, ketika counter timer atau pulse mengalami wrap, menyebabkan selisih waktu palsu yang mengganggu akumulasi.
Solusi wrap-aware delta mengatasi ini dengan menangani kondisi wrap secara eksplisit, melengkapi strategi sebelumnya seperti K_ACCUM untuk distribusi waktu antar controller.
Analisis Akar Masalah
Rentang 16-bit membatasi counter pada nilai 0–65.535, di mana rollover menyebabkan CURRENT < PREVIOUS.
Pendekatan naif menggunakan MOD — (CURRENT + 65536 - PREVIOUS) MOD 65536 — gagal di kondisi anomali non-monotonic (misalnya glitch hardware), menghasilkan delta besar seperti 65.486, interpretasi wrap palsu yang mempropagasi loncatan ke akumulator.
Pada DCS tanpa auto-promotion, perhitungan MOD juga rentan overflow pre-mod, memperbesar risiko delta negatif atau positif palsu setelah 65k pulsa.
Perbandingan Pendekatan
MOD efisien karena tanpa percabangan kondisional, tetapi rawan loncatan di anomali akibat ambiguitas modulo pada penurunan nilai (delta > separuh rentang 32.768).
Sebaliknya, IF-THEN-ELSE lebih eksplisit: langsung mengurangkan jika tidak wrap, atau menghitung sisa rentang + CURRENT + 1 jika wrap.
Keduanya menghasilkan nilai identik di kondisi normal, namun IF-THEN-ELSE unggul dalam proteksi (cutoff anomali) dan kemudahan debugging di DCS — memastikan akumulasi tetap terprediksi.
Catatan Teknis
K_DELTA versi 1.2 mengadopsi pendekatan IF-THEN-ELSE wrap-aware dengan cutoff 60 detik untuk loncatan ekstrem, menggunakan tipe LONG agar aman di Supcon.
Pendekatan ini menghilangkan loncatan akibat MOD dan menstabilkan delta untuk akumulasi yang dapat diprediksi.
Perlu dicatat bahwa masalah loncatan nilai sebenarnya bukan spesifik masalah K_DELTA, melainkan digunakan sebagai contoh kasus untuk menjelaskan perbedaan perilaku antara operator MOD dan logika IF-THEN-ELSE.
Dalam sistem sebenarnya, K_DELTA berfungsi sebagai time backbone — modul pusat yang menyediakan nilai delta waktu secara global bagi berbagai Function Block lain seperti K_ACCUM. Dengan cara ini, setiap K_ACCUM tidak perlu lagi menghitung delta waktunya masing-masing, sehingga struktur kode menjadi lebih sederhana, efisien, dan mudah diaudit.
Diagram Alur
Validasi Independen dengan Simulasi
Simulasi pada lingkungan mirip PLC menunjukkan loncatan MOD pada non-wrap decrease (delta = 65486 > 32768), sedangkan IF menghasilkan nilai yang sama namun dapat diproteksi dengan cutoff.
Tes konfirmasi membuktikan eliminasi loncatan dan akumulasi yang tetap stabil.
| Deskripsi | CURRENT | PREVIOUS | Delta MOD | Delta IF | Match? | Catatan |
|---|---|---|---|---|---|---|
| Normal increase | 100 | 50 | 50 | 50 | Ya | Aman |
| Wrap-around | 10 | 65530 | 16 | 16 | Ya | Rollover benar |
| Non-wrap decrease (anomaly) | 50 | 100 | 65486 | 65486 | Ya | Loncatan besar (>32.768, wrap palsu) |
| Exact wrap | 0 | 65535 | 1 | 1 | Ya | Aman |
| No change | 65535 | 65535 | 0 | 0 | Ya | Stabil |
Validasi Tambahan
Pengujian lanjutan pada K_DELTA menunjukkan hasil konsisten dengan desain: tidak ada loncatan delta meski terjadi wrap atau non-monotonic glitch.
Simulasi dilakukan pada 1000 siklus penuh dengan timer 16-bit rollover, serta injeksi anomali penurunan acak (10%). Semua kasus menghasilkan delta stabil, cutoff bekerja sesuai ambang 60 detik, dan alarm aktif hanya pada kondisi valid.
| Deskripsi | TIMER_IN | LAST_IN | Startup? | DELTA_OUT | ALARM_OUT | Raw Delta | Status |
|---|---|---|---|---|---|---|---|
| Normal increase | 100 | 50 | No | 50 | False | 50 | OK |
| Wrap-around | 10 | 65530 | No | 16 | False | 16 | OK |
| Non-wrap decrease (anomaly) | 50 | 100 | No | 0 | True | 65486 | OK |
| Exact wrap | 0 | 65535 | No | 1 | False | 1 | OK |
| No change | 65535 | 65535 | No | 0 | False | 0 | OK |
| Startup | 0 | 0 | Yes | 0 | False | 0 | OK |
| Large delta >60 | 200 | 100 | No | 0 | True | 100 | OK |
Hasil ringkas: Semua test pass 100%. Tidak ada loncatan, cutoff bekerja, dan akumulasi tetap konsisten hingga 1000 siklus penuh.
Simulasi lanjutan menunjukkan raw delta besar (misalnya 65.486) dipotong otomatis menjadi nol dengan alarm aktif — tanpa propagasi loncatan ke akumulator.
Interpretasi
Pendekatan MOD cenderung menghasilkan delta besar di kondisi anomali karena ambiguitas hasil modulo.
Sementara kombinasi IF-THEN-ELSE dengan cutoff mencegah propagasi loncatan dan menjaga integritas akumulasi jangka panjang.
Kesimpulan
Loncatan delta pada counter 16-bit berhasil diatasi oleh K_DELTA versi 1.2 melalui pendekatan IF-THEN-ELSE, yang menggantikan kelemahan MOD tanpa efek loncatan palsu.
Metode ini penting untuk menjaga keandalan sistem akumulasi dan runtime di lingkungan DCS yang sensitif terhadap rollover counter.
Catatan: Simulasi mengikuti aritmetika standar IEC 61131.
Appendix A — Listing Kode K_DELTA
(*==============================================================================
FB Name : K_DELTA
Purpose : Menghitung delta waktu wrap-aware dari timer utama (UINT 16-bit)
Author : Ketut Kumajaya
Version : 1.2 (IF-THEN-ELSE, no MOD)
Date : 07/10/2025
Input : TIMER_IN (UINT), LAST_IN (UINT)
Output : DELTA_OUT (UINT), LAST_OUT (UINT), ALARM_OUT (BOOL)
Notes : - IF-THEN-ELSE untuk hindari loncatan MOD
- Cutoff 60 detik; LONG untuk Supcon-safe
- Persisten global LAST_IN/OUT
==============================================================================*)
FUNCTION_BLOCK K_DELTA
VAR_INPUT
TIMER_IN : UINT;
LAST_IN : UINT;
END_VAR
VAR_OUTPUT
DELTA_OUT : UINT;
LAST_OUT : UINT;
ALARM_OUT : BOOL;
END_VAR
VAR
Delta : LONG;
StartupFlag : BOOL;
END_VAR
StartupFlag := g_bColdStartup OR g_bHotStartup OR g_bDownUsrPrgFlag OR g_bDownCfgFlag;
IF StartupFlag THEN
Delta := 0;
ALARM_OUT := FALSE;
g_bColdStartup := FALSE;
g_bHotStartup := FALSE;
g_bDownUsrPrgFlag := FALSE;
g_bDownCfgFlag := FALSE;
ELSE
IF TIMER_IN >= LAST_IN THEN
Delta := ULONG_TO_LONG(UINT_TO_ULONG(TIMER_IN)) - ULONG_TO_LONG(UINT_TO_ULONG(LAST_IN));
ELSE
Delta := ULONG_TO_LONG(UINT_TO_ULONG(65535 - LAST_IN)) + ULONG_TO_LONG(UINT_TO_ULONG(TIMER_IN)) + 1;
END_IF;
IF Delta > 60 THEN
Delta := 0;
ALARM_OUT := TRUE;
ELSE
ALARM_OUT := FALSE;
END_IF;
END_IF;
DELTA_OUT := ULONG_TO_UINT(LONG_TO_ULONG(Delta));
LAST_OUT := TIMER_IN;
END_FUNCTION_BLOCK