First Trip Logic Deterministik untuk Deteksi Trip Pertama Secara Akurat

Ditulis oleh Ketut Kumajaya — 2 November 2025

Pendahuluan

Dalam pengoperasian beberapa unit mesin secara bersamaan, seringkali terjadi trip yang tidak terduga. Yang paling krusial bagi operator dan engineer adalah mesin pertama yang mengalami trip, karena ini biasanya menjadi indikator awal masalah yang memerlukan tindakan cepat.

Logika standar yang hanya mencatat semua trip tanpa urutan seringkali membuat analisis menjadi lambat dan membingungkan. Untuk mengatasi hal ini, kita dapat menggunakan First Trip Logic (FTL) berbasis array dengan fitur rising edge detection, trip latching, dan pencatatan timestamp. Operator tetap bisa memantau status masing-masing mesin melalui pin input/output individual di diagram HMI.


Desain Arsitektur FTL

Solusi FTL terdiri dari dua bagian:

  1. Core Function Block (K_FTL)

    • Mendeteksi trip pertama dari sejumlah mesin (default 8 channel).
    • Array-based input (RunInputs[1..8]).
    • Rising edge detection per channel untuk menghindari false trip.
    • TripLatched[] menandai mesin yang sudah trip sampai reset manual.
    • FirstTripIndex menunjukkan mesin pertama yang trip.
    • TripTime[] mencatat timestamp trip untuk audit trail.
    • Deterministik: prioritas indeks mesin dijaga.
  2. Wrapper Function Block (K_FTLW)

    • Memetakan 8 input/output individual (In1..In8RunInputs[1..8], TripLatched[1..8]Out1..Out8).
    • Operator tetap dapat memantau status masing-masing mesin di diagram HMI.
    • Reset diteruskan ke core FB, dengan edge-hold untuk mencegah false first trip.

Alur Logika:

  • Reset → inisialisasi semua trip latched & timestamp → monitoring RunInputs → deteksi rising edge → latching → pencatatan FirstTripIndex & TripTime.
%%{init: {'themeVariables': { 'primaryColor': '#FF5C6A', 'edgeLabelBackground':'#ffffff', 'tertiaryColor': '#A5D8FF'}}}%% flowchart TD A(["Start / Reset?"]) -- "Reset=TRUE" --> B(["Set FirstTripIndex=0, TripLocked=FALSE, ResetLatch=TRUE"]) B --> C(["Inisialisasi TripLatched i=FALSE, TripTime i=DT#1970-01-01, PrevInputs i=RunInputs i"]) C --> D(["End Reset"]) A -- "Reset=FALSE" --> E(["ResetLatch=FALSE"]) E --> F(["Loop i=1..8"]) F --> G{"RunInputs i=TRUE AND PrevInputs i=FALSE?"} G -- Yes --> H(["TripLatched i=TRUE, TripTime i=SYSTIME()"]) H --> I{"TripLocked=FALSE?"} I -- Yes --> J(["FirstTripIndex=i, TripLocked=TRUE"]) I -- No --> K(["Do nothing"]) G -- No --> K K --> L(["PrevInputs i=RunInputs i"]) L --> M{"i<8?"} M -- Yes --> F M -- No --> N(["End Loop / Update Outputs"]) N --> O(["Map TripLatched i → Out1..Out8"]) O --> P(["Operator Monitoring Diagram"]) A:::Rose B:::Ash C:::Ash D:::Rose E:::Sky F:::Rose G:::Peach H:::Sky I:::Peach J:::Sky K:::Sky L:::Sky M:::Peach N:::Rose O:::Pine P:::Pine classDef Rose stroke-width:2px, stroke:#A0002E, fill:#FF5C6A, color:#FFFFFF classDef Ash stroke-width:1px, stroke:#666666, fill:#DDDDDD, color:#000 classDef Sky stroke-width:1px, stroke:#1E3A8A, fill:#A5D8FF, color:#1E3A8A classDef Peach stroke-width:2px, stroke:#FF8C00, fill:#FFD699, color:#8B4513 classDef Pine stroke-width:2px, stroke:#006400, fill:#3CB371, color:#FFFFFF
Diagram First Trip Logic: Alur logika untuk mendeteksi trip pertama dari sekumpulan mesin

Kode Implementasi

Berikut implementasi dalam Structured Text (ST) berdasarkan standar IEC 61131-3, dapat digunakan langsung pada PLC atau DCS yang kompatibel.

Core Function Block – K_FTL

(*
===========================================================
  K_FTL : Ketut - First Trip Logic
  Versi  : 1.0
  Author : Ketut Kumajaya
  Scope  : Function block untuk mendeteksi trip pertama
           dari sekumpulan mesin (default 8 input).
  Fitur  :
    - Array-based input (RunInputs[1..8])
    - Rising edge detection per channel
    - TripLatched[] → status trip latched sampai reset manual
    - FirstTripIndex → menunjukkan mesin pertama yang trip
    - TripTime[] → timestamp absolut untuk audit trail
    - Reset manual menghapus semua status
  Catatan :
    - Deterministik dengan prioritas indeks array
    - Nama mengikuti istilah industri: First Trip Logic
    - **Input RunInputs[i] harus aktif HIGH saat trip terjadi**
      Jika sinyal asli aktif LOW, lakukan inversi sebelum masuk FB:
        RunInputs[i] := NOT OriginalSignal[i]
===========================================================
*)

FUNCTION_BLOCK K_FTL
VAR_INPUT
    RunInputs : ARRAY[1..8] OF BOOL;  
    Reset     : BOOL;
END_VAR

VAR_OUTPUT
    FirstTripIndex : INT;                
    TripLatched    : ARRAY[1..8] OF BOOL;
    TripTime       : ARRAY[1..8] OF DT;  
END_VAR

VAR
    PrevInputs : ARRAY[1..8] OF BOOL;
    TripLocked : BOOL;
    ResetLatch : BOOL; 
    i          : INT;
END_VAR

IF Reset THEN
    FirstTripIndex := 0;
    TripLocked := FALSE;
    ResetLatch := TRUE;

    FOR i := 1 TO 8 DO
        TripLatched[i] := FALSE;
        TripTime[i] := DT#1970-01-01-00:00:00;
        PrevInputs[i] := RunInputs[i];  
    END_FOR;
ELSE
    ResetLatch := FALSE;

    FOR i := 1 TO 8 DO
        IF (NOT ResetLatch) AND (RunInputs[i] = TRUE) AND (PrevInputs[i] = FALSE) THEN
            TripLatched[i] := TRUE;
            TripTime[i] := SYSTIME();  

            IF NOT TripLocked THEN
                FirstTripIndex := i;
                TripLocked := TRUE;
            END_IF;
        END_IF;
        PrevInputs[i] := RunInputs[i];
    END_FOR;
END_IF;
END_FUNCTION_BLOCK

Wrapper Function Block – K_FTLW

(*===========================================================
  K_FTLW : Ketut - First Trip Logic Wrapper
  Versi  : 1.0
  Author : Ketut Kumajaya
  Scope  : Wrapper untuk K_FTL agar operator melihat
           8 pin input/output individual di diagram.
  Fitur  :
    - Memanggil K_FTL (array-based) di dalamnya
    - Memetakan In1..In8 → RunInputs[1..8]
    - Memetakan Out1..Out8 ← TripLatched[1..8]
    - Reset manual diteruskan ke core FB
  Catatan :
    - Operator tetap bisa memantau status individual
    - Deterministik dengan prioritas indeks array
===========================================================*)

FUNCTION_BLOCK K_FTLW
VAR_INPUT
    In1, In2, In3, In4, In5, In6, In7, In8 : BOOL;
    Reset : BOOL;
END_VAR

VAR_OUTPUT
    FirstTripIndex : INT;
    Out1, Out2, Out3, Out4, Out5, Out6, Out7, Out8 : BOOL;
END_VAR

VAR
    Core     : K_FTL;               
    RunArray : ARRAY[1..8] OF BOOL;
    i        : INT;
END_VAR

(* Map inputs *)
RunArray[1] := In1;
RunArray[2] := In2;
RunArray[3] := In3;
RunArray[4] := In4;
RunArray[5] := In5;
RunArray[6] := In6;
RunArray[7] := In7;
RunArray[8] := In8;

(* Call core FB *)
Core(RunInputs := RunArray, Reset := Reset);

(* Map outputs *)
FirstTripIndex := Core.FirstTripIndex;
Out1 := Core.TripLatched[1];
Out2 := Core.TripLatched[2];
Out3 := Core.TripLatched[3];
Out4 := Core.TripLatched[4];
Out5 := Core.TripLatched[5];
Out6 := Core.TripLatched[6];
Out7 := Core.TripLatched[7];
Out8 := Core.TripLatched[8];

Verifikasi melalui Simulasi

Untuk memvalidasi logika K_FTL, simulasi dilakukan menggunakan Python (mengadaptasi kode Structured Text ke class sederhana). Test case mencakup reset, no-trip, single trip, multiple trip, dan reset ulang. Timestamp menggunakan waktu real (mirip SYSTIME() di PLC).

Untuk memastikan reprodusibilitas hasil dan mempercepat validasi, simulasi dilakukan di lingkungan Jupyter Notebook, yang memungkinkan eksekusi kode Python dan visualisasi hasil dalam satu lingkungan interaktif. Pendekatan ini memudahkan verifikasi logika First Trip Logic secara real-time, karena setiap perubahan nilai input dapat langsung diamati melalui output dan grafik timeline tanpa perlu kompilasi ulang atau deploy ke PLC.

Test Case Deskripsi Input (RunInputs, Channel 1-8) Reset Input Hasil Utama
1: Reset Awal Inisialisasi semua status [False x8] True FirstTripIndex=0
TripLatched=[False x8]
TripTime=[1970-01-01 x8]
2: No Trip Monitoring tanpa perubahan [False x8] False FirstTripIndex=0
TripLatched=[False x8]
TripTime=[1970-01-01 x8]
3: Single Trip (Channel 4) Rising edge di channel 4 [False, False, False, True x4] False FirstTripIndex=4
TripLatched=[False, False, False, True x4]
TripTime=[1970-01-01 x6, T1 untuk ch4]
4: Multiple Trip Tambah rising edge di semua channel [True x8] False FirstTripIndex=4 (tetap locked!)
TripLatched=[True x8]
TripTime=[T2 x7, T1 untuk ch4]
5: Reset Ulang Reset setelah multiple trip [False x8] True FirstTripIndex=0
TripLatched=[False x8]
TripTime=[1970-01-01 x8]

Catatan Simulasi: Rising edge detection dan prioritas indeks terjaga. Timestamp T1/T2 adalah waktu real saat trip.

Klik untuk Lihat Script Python Lengkap (Simulasi Real-Time)
import time, random
from datetime import datetime

class K_FTL:
    """
    K_FTL — First Trip Logic Deterministik

    Simulasi Function Block (FB) untuk mendeteksi sinyal trip pertama
    di antara beberapa input digital, lalu mengunci (lock) hasilnya
    agar tetap konsisten sampai di-reset.
    """

    def __init__(self):
        # --- Variabel internal ---
        self.FirstTripIndex = 0         # Channel pertama yang trip (1-based)
        self.TripLatched = [False] * 8  # Status latched tiap channel
        self.TripTime = [None] * 8      # Timestamp trip per channel
        self.PrevInputs = [False] * 8   # Nilai input sebelumnya
        self.TripLocked = False         # Lock ketika first trip sudah ditentukan
        self.ResetLatch = False         # Status reset terakhir

    def execute(self, RunInputs, Reset):
        """
        Jalankan logika FTL.
        RunInputs: list[bool] panjang 8 — status input digital
        Reset: bool — sinyal reset latch
        """
        current_real_time = time.time()

        if Reset:
            # Reset semua status internal
            self.FirstTripIndex = 0
            self.TripLocked = False
            self.ResetLatch = True
            for i in range(8):
                self.TripLatched[i] = False
                self.TripTime[i] = None
                self.PrevInputs[i] = RunInputs[i]
        else:
            self.ResetLatch = False
            for i in range(8):
                # Deteksi rising edge: FALSE → TRUE
                if not self.ResetLatch and RunInputs[i] and not self.PrevInputs[i]:
                    self.TripLatched[i] = True
                    self.TripTime[i] = current_real_time
                    # Catat trip pertama hanya sekali
                    if not self.TripLocked:
                        self.FirstTripIndex = i + 1
                        self.TripLocked = True
                self.PrevInputs[i] = RunInputs[i]


def print_status(ftl, test_name):
    """
    Print status internal FB dengan format waktu yang mudah dibaca.
    """
    print(f"\n{test_name}:")
    print(f"  FirstTripIndex: {ftl.FirstTripIndex}")
    print(f"  TripLatched   : {ftl.TripLatched}")
    formatted_times = [
        datetime.fromtimestamp(t).strftime('%Y-%m-%d %H:%M:%S')
        if t else '1970-01-01' for t in ftl.TripTime
    ]
    print(f"  TripTime      : {formatted_times}")

# --- Simulasi FTL ---
ftl = K_FTL()

# Test 1: Reset Awal
RunInputs = [False] * 8
ftl.execute(RunInputs, True)
print_status(ftl, "Test 1 - Reset Awal")

# Test 2: No Trip
ftl.execute(RunInputs, False)
print_status(ftl, "Test 2 - No Trip")

# Test 3: Single Trip (ubah channel sesuai kebutuhan)
trip_channel = 4
RunInputs[trip_channel - 1] = True
ftl.execute(RunInputs, False)
print_status(ftl, f"Test 3 - Single Trip Channel {trip_channel}")

# Test 4: Multiple Trip dengan delay acak 0.3–1.5s dan channel acak
channels = list(range(8))
random.shuffle(channels)  # urutan channel diacak
delays = [random.uniform(0.3, 1.5) for _ in range(8)]

print("\nTest 4 - Multiple Trip dengan delay acak dan channel acak:")
for ch, delay in zip(channels, delays):
    if not ftl.TripLatched[ch]:
        RunInputs[ch] = True
        time.sleep(delay)
        ftl.execute(RunInputs, False)
        print(f"  Tripped ch{ch+1} after {delay:.2f}s at {datetime.now().strftime('%H:%M:%S.%f')[:-3]}")
print_status(ftl, "Test 4 - Multiple Trip (random delay & channel acak)")

# Test 5: Reset Ulang
ftl.execute(RunInputs, True)
print_status(ftl, "Test 5 - Reset Ulang")

Visualisasi Hasil Simulasi

Script berikut untuk membuat visualisasi timeline terjadinya trip pada masing-masing channel berdasarkan hasil eksekusi class K_FTL di cell Jupyter Notebook sebelumnya. Setiap batang horizontal merepresentasikan waktu relatif terhadap trip pertama, sementara warna menunjukkan identitas channel yang mengalami trip.

Catatan: Pastikan bagian Test 5: Reset Ulang pada cell simulasi sebelumnya dihapus atau tidak dijalankan sebelum melakukan visualisasi, agar hasil grafik tetap merepresentasikan kondisi trip terakhir dengan benar.

from datetime import datetime
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import seaborn as sns

# --- Plotting ---
rel_times = [max(0.1, (ftl.TripTime[i] - ftl.TripTime[ftl.FirstTripIndex-1] if ftl.TripTime[i] else 0.1))
             for i in range(8)]

plt.rcParams['svg.fonttype'] = 'none'  # simpan teks sebagai teks, bukan path
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Arial']
plt.rcParams['font.size'] = 9  # basis font size lebih kecil
sns.set_style("whitegrid")
plt.style.use('seaborn-v0_8-darkgrid')
fig, ax = plt.subplots(figsize=(10,5))
colors = sns.color_palette("Spectral", 8)
colors[ftl.FirstTripIndex-1] = 'red'

for i in range(8):
    ax.barh(i, rel_times[i], color=colors[i],
            edgecolor='darkred' if i==ftl.FirstTripIndex-1 else 'navy',
            linewidth=1.5 if i==ftl.FirstTripIndex-1 else 1)
    if ftl.TripTime[i]:
        ax.text(rel_times[i]+0.05, i,
                datetime.fromtimestamp(ftl.TripTime[i]).strftime('%H:%M:%S'),
                va='center', ha='left', fontsize=8)

# Annotasi First Trip
ax.annotate(f'First Trip (Ch {ftl.FirstTripIndex})',
            xy=(rel_times[ftl.FirstTripIndex-1]+0.4, ftl.FirstTripIndex-1),
            xytext=(rel_times[ftl.FirstTripIndex-1]+1, ftl.FirstTripIndex-1+0.05),
            arrowprops=dict(facecolor='black', shrink=0.05, width=1.5, headwidth=6),
            fontsize=9, color='red')

ax.set_yticks(range(8))
ax.set_yticklabels(range(1,9))
ax.invert_yaxis()
ax.set_xlabel('Detik dari Trip Pertama')
ax.set_title(f'Timeline Trip per Channel (First Trip: Ch {ftl.FirstTripIndex})')
ax.grid(axis='x', linestyle='--', alpha=0.45, color='gray')
ax.set_xlim(0, max(rel_times)+1)

# Legend lebih ringkas
legend_elements = [Line2D([0],[0], color=colors[i], lw=3, label=f'Ch {i+1}') for i in range(8)]
ax.legend(handles=legend_elements, loc='upper right', fontsize=8)

# Simpan dan tampilkan plot
plt.tight_layout()
plt.savefig('ftl_trip_timeline.svg', format='svg')
plt.savefig('ftl_trip_timeline.png', format='png', dpi=300)
plt.show()

Visualisasi Timeline Trip per Channel

Setelah logika First Trip Logic disimulasikan dan diverifikasi di Jupyter Notebook, hasilnya divisualisasikan dalam bentuk diagram waktu agar memudahkan interpretasi urutan trip secara visual. Grafik ini juga berfungsi sebagai bukti deterministik bahwa FirstTripIndex selalu konsisten dengan urutan aktual terjadinya trip.

Timeline Trip per Channel
Hasil Simulasi: Channel dengan titik waktu paling awal (contoh: Channel 4) ditetapkan sebagai First Trip

Manfaat dan Kelebihan

  • Human‑friendly: status mesin ditampilkan per pin di diagram HMI.
  • Audit-ready: timestamp tiap trip memudahkan analisis root-cause.
  • Deterministik: prioritas indeks terjaga; mesin pertama tercatat dengan benar.
  • Scalable: core FB bisa diperluas dari 8 ke 16 channel dengan minimal modifikasi.
  • Integrasi mudah: langsung bisa dipasang di DCS/PLC tanpa board tambahan.

Tips & Best Practices

  1. Pastikan RunInputs aktif sebelum monitoring untuk menghindari false trip saat startup.
  2. Lakukan reset saat kondisi aman agar first trip logika tidak salah deteksi.
  3. Periksa fungsi timestamp di DCS Anda (SYSTIME atau CURRENT_DATE_TIME).
  4. Simulasikan beberapa mesin trip secara bersamaan untuk memastikan FirstTripIndex berfungsi sesuai harapan.

Catatan:

  • FB ini menentukan first trip secara deterministik berdasarkan indeks array input.
  • Jika beberapa mesin trip hampir bersamaan, prioritas dipengaruhi posisi di array.
  • Untuk kondisi kritis, bisa menggunakan dual FB parallel:
    • Jika kedua FB menghasilkan FirstTripIndex sama → trip pertama valid.
    • Jika berbeda → kondisi perlu dianalisis lebih lanjut.

Penutup

Dengan menggunakan K_FTL, Anda dapat membuat monitoring mesin lebih aman, terstruktur, dan human‑friendly. Logika deterministik ini memastikan mesin pertama yang trip selalu tercatat dengan tepat, mempermudah troubleshooting dan audit. Logika sederhana seperti ini bisa menjadi pembeda antara sistem yang hanya menunjukkan gejala dan sistem yang benar-benar memberi tahu akar masalah.