Pendekatan Modular dengan Buffer Timer dan Klasifikasi Setelah Trip

Ditulis oleh Ketut Kumajaya | 16 Agustus 2025

Pendahuluan

Manajemen shutdown runtime dalam sistem DCS bertujuan mencatat setiap penghentian operasi secara akurat dan terklasifikasi, guna mendukung pelaporan Overall Equipment Effectiveness (OEE) yang valid. Dalam praktik, operator tidak selalu bisa segera mengklasifikasikan jenis shutdown saat trip terjadi, sehingga data downtime berisiko tercatat tidak sesuai.

Pendekatan modular ini menggunakan buffer runtime sebagai penampung sementara durasi shutdown. Buffer tidak harus langsung ditransfer ke kategori yang sesuai; transfer dapat dilakukan setelah situasi terkendali akibat trip, termasuk pasca plant restart. Namun, jika hingga terjadi trip berikutnya klasifikasi belum dilakukan, durasi shutdown dari kategori berbeda berpotensi tercampur di buffer, mengaburkan akar penyebab downtime pada laporan OEE. Karena itu, klasifikasi sesegera mungkin sangat dianjurkan.


Prinsip Kerja

  • Saat plant shutdown/trip: Sistem otomatis masuk buffer mode untuk mencatat durasi downtime. Operator memilih kategori shutdown sebelum plant start (peluang pertama). Jika terlewat, klasifikasi masih dapat dilakukan sekali setelah start (peluang kedua). Shutdown berikutnya tanpa klasifikasi akan potensial mencampurkan data dengan kategori berbeda, menurunkan akurasi OEE.
  • Setelah klasifikasi: Durasi di buffer dipindahkan ke kategori yang dipilih. Sistem melanjutkan pencatatan pada kategori yang sesuai jika shutdown masih berlanjut.
  • Perhitungan waktu: Durasi shutdown dihitung otomatis per kategori, termasuk total eksternal shutdown.
  • Retensi data: Semua status dan durasi tetap tersimpan meski plant restart.
--- config: look: handDrawn theme: neutral --- flowchart TD MULAI["Mulai"]:::startend --> A{"Plant shutdown/trip?"}:::decision A -- Tidak --> B["Monitor"]:::process --> SELESAI["Selesai"]:::startend A -- Ya --> D["Buffer aktif"]:::process D --> F{"Kategori diketahui?"}:::decision F -- Tidak --> G["Klasifikasikan saat aman"]:::process --> SELESAI F -- Ya --> H["Pilih kategori"]:::process H --> I["Checklist → start/restart"]:::process --> SELESAI["Kembali ke awal"] %% Style definitions classDef decision fill:#ffe0b3,stroke:#cc7a00,stroke-width:2px classDef process fill:#cce5ff,stroke:#004085,stroke-width:2px classDef startend fill:#d4edda,stroke:#155724,stroke-width:2px,font-weight:bold
Panduan Cepat Alur Keputusan Kategori Shutdown

Kategori Shutdown

Type Kategori Deskripsi
0 Idle Tidak ada shutdown aktif
1 Buffered Shutdown Belum diklasifikasikan; durasi disimpan di buffer
2 Agreed Shutdown (AG) Shutdown terencana dan disepakati
3 Random Shutdown (RS) Shutdown mendadak tanpa perencanaan
4 External – Electricity Trip (EG–ET) Trip akibat gangguan listrik eksternal
5 External – Lack of Order (EG–LO) Tidak ada pesanan produksi
6 External – Water Shortage (EG–WS) Pasokan air tidak mencukupi

Variabel & Fungsi Utama

Variabel Tipe Deskripsi
OEE_RUN BOOL Status plant (TRUE = running, FALSE = trip).
OEE_RUP BOOL Trigger start plant (R_TRIG + TP). Reset status trip & kategori.
OEE_RDN BOOL Trigger trip plant (F_TRIG + TP). Aktifkan buffer jika belum ada trip aktif.
FLG_IN BOOL Loop input flag trip global → FB.
TYP_IN UINT Loop input kategori shutdown global → FB.
FLG_OUT BOOL Loop output flag trip FB → global.
TYP_OUT UINT Loop output kategori shutdown FB → global.
OEE_BE..WE BOOL Enable akumulasi per kategori (Buffered, Agreed, Random, ElecTrip, NoOrder, WaterShort).
OEE_AD..WD BOOL Pulse transfer buffer → kategori

Fungsi / Rutin Utama

  • Deteksi Start (OEE_RUP)
    Reset status trip, normalisasi kategori, siap untuk rekap eksternal.

  • Deteksi Trip (OEE_RDN)
    Masuk buffer mode, snapshot timer, trigger akumulasi awal.

  • Proteksi Status
    Normalisasi typ (Idle → Buffered, atau reset jika >6).

  • Transfer Buffer → Kategori
    Pulse OEE_AD..WD sesuai kategori, reset buffer setelah transfer.

  • Akumulasi Waktu
    Enable OEE_BE..WE saat OEE_RUN = FALSE, sesuai kategori aktif.


Alur Operasional

1. Plant Berjalan (OEE_RUN = TRUE)

  • Sistem dalam kondisi normal, tidak ada akumulasi downtime.
  • Jika OEE_RUP = TRUE (restart):
    • Reset status trip (FLG_OUT := FALSE, TYP_OUT := 0).
    • Normalisasi kategori shutdown.
    • Buffer dikosongkan, siap untuk siklus berikutnya.

2. Plant Trip (OEE_RUN = FALSE)

  • Jika OEE_RDN = TRUE dan belum ada trip aktif:
    • Sistem masuk buffer mode (TYP_OUT := 1, FLG_OUT := TRUE).
    • Timer buffer mulai mencatat durasi downtime.
  • Jika FLG_OUT = TRUE dan TYP_OUT = 1:
    • Buffer terus mengakumulasi waktu shutdown.
  • Jika FLG_OUT = FALSE dan TYP_OUT ∈ [2..6]:
    • Akumulasi langsung ke kategori yang dipilih.

3. Transfer Buffer → Kategori

  • Saat operator memilih kategori shutdown (TYP_OUT ∈ [2..6]):
    • Pulse transfer trigger (OEE_AD..WD := TRUE) ke EN K_ADD_ACCUM, mengalihkan durasi buffer ke kategori.
    • Buffer dikosongkan menggunakan ENO K_ADD_ACCUM di atas, diisolasi MOVE_BOOL. Status trip dinormalisasi.

4. Akumulasi Waktu

  • Selama OEE_RUN = FALSE:
    • OEE_BE aktif jika masih di buffer.
    • OEE_AE..WE aktif sesuai kategori shutdown yang dipilih. Ini dihubungkan ke ENABLE K_ACCUM.

5. Proteksi Status

  • Jika TYP_OUT = 0 saat trip → otomatis dinormalisasi ke buffer (TYP_OUT := 1).
  • Jika TYP_OUT > 6 → diset ulang ke 0 (jika RUN) atau 1 (jika trip).

Catatan

  • Rekap total runtime eksternal (ET, LO, WS) dilakukan di luar Function Block. Beberapa Function Block lain digunakan, diantaranya: K_ACCUM, K_ADD_ACCUM, dan K_SUB_ACCUM.
  • Operator wajib memilih kategori shutdown sebelum plant start untuk mencegah tercampurnya durasi antar kategori.

Program DCS dan Flowchart

Program DCS dibungkus dalam Function Block K_OEE sehingga ringkas, modular, traceable, dan portabel—mudah dipindahkan ke DCS/PLC lain.
Pendekatan ini menegaskan bahwa K_OEE berperan murni sebagai state machine, sementara eksekusi teknis didistribusikan ke blok lain sesuai fungsinya:

  • K_ACCUM → menangani akumulasi waktu shutdown per kategori
  • K_ADD_ACCUM → menangani transfer durasi dari buffer ke kategori terpilih
  • K_SUB_ACCUM → menangani reset buffer setelah transfer

Dengan pemisahan ini, setiap blok memiliki tanggung jawab yang jelas (separation of concerns), sehingga logika lebih mudah ditelusuri, diuji, dan diadaptasi lintas plant.

Flowchart menjadi referensi hidup yang memastikan setiap revisi logika tetap dapat ditelusuri, diaudit, dan diajarkan lintas operator.

Program DCS – Manajemen OEE Shutdown
(*
===============================================================================
 Function Block : K_OEE
 Deskripsi      : Modul manajemen shutdown OEE berbasis buffer -> kategori.
                  FB ini mengelola transisi event Trip/Restart, proteksi status,
                  transfer buffer ke kategori shutdown, serta akumulasi waktu
                  saat plant tidak RUN. Dirancang modular & reusable antar plant.

 Penulis        : Ketut Kumajaya
 Kontributor    : Copilot (Microsoft AI)
 Versi          : 2.0 (FB version)
 Tanggal        : 28/09/2025

 Interface I/O  :
   VAR_INPUT
     OEE_RUN    - Status plant run
     OEE_RUP    - Trigger restart plant menggunakan R_TRIG + TP 1000ms eksternal
     OEE_RDN    - Trigger trip plant menggunakan F_TRIG + TP 1000ms eksternal
     FLG_IN     - Flag event aktif (loop input dari global/HMI)
     TYP_IN     - Tipe shutdown aktif (loop input dari global/HMI)

   VAR_OUTPUT
     OEE_BE..WE - Enable accumulator K_ACCUM (Buffered, Agreed, Random,
                  ElecTrip, NoOrder, WaterShort)
     OEE_AD..WD - Transfer flag buffer -> kategori menggunakan TP 1000ms
                  sebagai pulse untuk K_ADD_ACCUM
     FLG_OUT    - Flag event aktif (loop output ke global/HMI)
     TYP_OUT    - Tipe shutdown aktif (loop output ke global/HMI)

 Catatan        :
   - OEE_FLG dan OEE_TYP tetap global untuk HMI, dihubungkan via _IN/_OUT.
   - FB dapat di‑instantiate per line/plant secara independen.
   - Reset accumulator buffer dilakukan di luar FB setelah transfer. Isolasi
     ENO K_ADD_ACCUM dengan MOVE_BOOL.
===============================================================================
*)

FUNCTION_BLOCK K_OEE
VAR_INPUT
    OEE_RUN    : BOOL;   (* Status plant run *)
    OEE_RUP    : BOOL;   (* Trigger restart plant *)
    OEE_RDN    : BOOL;   (* Trigger trip plant *)

    FLG_IN : BOOL;   (* Loop input dari global *)
    TYP_IN : UINT;   (* Loop input dari global *)
END_VAR

VAR_OUTPUT
    (* Enable accumulator *)
    OEE_BE, OEE_AE, OEE_RE : BOOL;
    OEE_EE, OEE_LE, OEE_WE : BOOL;

    (* Transfer flag *)
    OEE_AD, OEE_RD, OEE_ED : BOOL;
    OEE_LD, OEE_WD         : BOOL;

    (* Loop output ke global *)
    FLG_OUT : BOOL;
    TYP_OUT : UINT;
END_VAR

VAR
    flg : BOOL;   (* internal mirror dari FLG_IN *)
    typ : UINT;   (* internal mirror dari TYP_IN *)
END_VAR

(* ========================================================================== *)
(*  OEE Shutdown Management Logic                                             *)
(* ========================================================================== *)

(* Mirror input ke internal state *)
flg := FLG_IN;
typ := TYP_IN;

(* Reset semua enable accumulator di awal siklus *)
OEE_BE := FALSE; OEE_AE := FALSE; OEE_RE := FALSE;
OEE_EE := FALSE; OEE_LE := FALSE; OEE_WE := FALSE;

(* Reset semua enable transfer di awal siklus *)
OEE_AD := FALSE; OEE_RD := FALSE; OEE_ED := FALSE;
OEE_LD := FALSE; OEE_WD := FALSE;

(* EVENT: Restart Plant *)
IF OEE_RUP THEN
    OEE_BE := FALSE; OEE_AE := FALSE; OEE_RE := FALSE;
    OEE_EE := FALSE; OEE_LE := FALSE; OEE_WE := FALSE;
    IF (typ >= 2) AND (typ <= 6) THEN
        flg := FALSE; typ := 0;
        (* Reset status trip & trigger rekap eksternal *)
    END_IF;
END_IF;

(* EVENT: Trip Plant *)
IF OEE_RDN THEN
    IF NOT flg THEN
        flg := TRUE; typ := 1;
        (* Masuk buffer mode, snapshot timer, trigger rekap *)
    END_IF;
END_IF;

(* PROTEKSI: konsistensi status *)
IF flg AND (typ = 0) THEN
    typ := 1; 
    (* Normalisasi tipe idle -> buffered *)
END_IF;

(* PROTEKSI: Normalisasi tipe shutdown *)
IF typ > 6 THEN
    IF OEE_RUN THEN typ := 0; ELSE typ := 1; END_IF;
END_IF;

(* TRANSFER: Buffer ke kategori *)
IF flg AND (typ >= 2) AND (typ <= 6) THEN
    OEE_BE := FALSE;
    CASE typ OF
        2: OEE_AD := TRUE; (* Agreed *)
        3: OEE_RD := TRUE; (* Random *)
        4: OEE_ED := TRUE; (* ElecTrip *)
        5: OEE_LD := TRUE; (* NoOrder *)
        6: OEE_WD := TRUE; (* WaterShort *)
    END_CASE;
    flg := FALSE;
    IF OEE_RUN THEN typ := 0; END_IF;
END_IF;

(* AKUMULASI: Dilakukan saat RUN=FALSE *)
IF NOT OEE_RUN THEN
    (* Buffered *)
    IF flg AND (typ = 1) THEN
        OEE_BE := TRUE;
    END_IF;

    (* Kategori 2..6 *)
    IF NOT flg THEN
        CASE typ OF
            2: OEE_AE := TRUE;
            3: OEE_RE := TRUE;
            4: OEE_EE := TRUE;
            5: OEE_LE := TRUE;
            6: OEE_WE := TRUE;
        END_CASE;
    END_IF;
END_IF;

(* Mirror internal state ke output *)
FLG_OUT := flg;
TYP_OUT := typ;

END_FUNCTION_BLOCK

Flowchart – Alur Logika Shutdown
--- config: look: handDrawn theme: neutral layout: fixed --- flowchart TD classDef mulai fill:#d4f4dd,stroke:#2e7d32,stroke-width:1px,color:#000 classDef decision fill:#fff3cd,stroke:#f0ad4e,stroke-width:1px,color:#000 classDef process fill:#d9edf7,stroke:#31708f,stroke-width:1px,color:#000 classDef akhir fill:#f5f5f5,stroke:#777,stroke-width:1px,color:#000 A["Mulai siklus"]:::mulai --> B["Mirror Input: FLG_IN, TYP_IN"]:::process B --> C["Reset semua pengaktif accumulator & transfer"]:::process %% Event Handling subgraph S1[" "] C --> D{"OEE_RUP?"}:::decision D -->|Ya| E1{"typ >= 2 AND typ <= 6?"}:::decision E1 -->|Ya| E2["Reset status trip: flg:=FALSE, typ:=0"]:::process E1 -->|Tidak| E3["Reset semua accumulator"]:::process D -->|Tidak| F{"OEE_RDN AND NOT flg?"}:::decision E2 --> Q E3 --> Q F -->|Ya| G["Set flg:=TRUE, typ:=1 (Buffer Mode)"]:::process F -->|Tidak| H["Proteksi Status"]:::process G --> Q end style S1 fill:transparent,stroke:transparent %% Proteksi subgraph S2[" "] H --> I1{"flg AND typ = 0?"}:::decision I1 -->|Ya| J1["typ := 1"]:::process I1 -->|Tidak| I2{"typ > 6?"}:::decision I2 -->|Ya| J2{"OEE_RUN?"}:::decision J2 -->|Ya| J3["typ := 0"]:::process J2 -->|Tidak| J4["typ := 1"]:::process I2 -->|Tidak| K J1 --> K J3 --> K J4 --> K end style S2 fill:transparent,stroke:transparent %% Transfer Buffer ke Kategori subgraph S3[" "] K{"flg AND typ >= 2 AND typ <= 6?"}:::decision K -->|typ=2| L1["OEE_BE:=FALSE; Pulse Transfer ke Agreed"]:::process K -->|typ=3| L2["OEE_BE:=FALSE; Pulse Transfer ke Random"]:::process K -->|typ=4| L3["OEE_BE:=FALSE; Pulse Transfer ke ElecTrip"]:::process K -->|typ=5| L4["OEE_BE:=FALSE; Pulse Transfer ke NoOrder"]:::process K -->|typ=6| L5["OEE_BE:=FALSE; Pulse Transfer ke WaterShort"]:::process L1 --> M["flg:=FALSE; IF OEE_RUN THEN typ:=0"]:::process L2 --> M L3 --> M L4 --> M L5 --> M M --> N K -->|Tidak| N end style S3 fill:transparent,stroke:transparent %% Akumulasi subgraph S4[" "] N{"OEE_RUN = FALSE?"}:::decision N -->|Ya| N1{"flg = TRUE AND typ = 1?"}:::decision N1 -->|Ya| O1["Enable Buffered Accumulator"]:::process N1 -->|Tidak| N2{"flg = FALSE AND typ >= 2 AND typ <= 6?"}:::decision N2 -->|typ=2| O2["Enable Agreed Accumulator"]:::process N2 -->|typ=3| O3["Enable Random Accumulator"]:::process N2 -->|typ=4| O4["Enable ElecTrip Accumulator"]:::process N2 -->|typ=5| O5["Enable NoOrder Accumulator"]:::process N2 -->|typ=6| O6["Enable WaterShort Accumulator"]:::process N -->|Tidak| P["Skip Akumulasi"]:::process N2 -->|Tidak| P end style S4 fill:transparent,stroke:transparent %% Output & Loop O1 --> Q["Mirror ke FLG_OUT, TYP_OUT"]:::process O2 --> Q O3 --> Q O4 --> Q O5 --> Q O6 --> Q P --> Q Q --> R["Kembali ke siklus awal"]:::akhir
Flowchart – Alur Hubungan Antar FB
--- config: look: handDrawn theme: neutral layout: fixed --- flowchart LR A["TIMER_IN
(UINT Global)"] --> B["K_DELTA"] C["OEE_RUN
RUP/RDN Eksternal
FLG_IN/TYP_IN HMI"] --> KOEE["K_OEE
(State Machine + Transfer Flow)"] B --> E["DELTA_OUT
(UINT Shared, ~1s/cycle)"] KOEE --> G["ENABLEs
(BE=1 Buffered, AE=2 Agreed,
RE=3 Random, EE=4 ElecTrip,
LE=5 NoOrder, WE=6 WaterShort)"] & H["PULSEs (Trigger)
(AD=2 Agreed, RD=3 Random,
ED=4 ElecTrip, LD=5 NoOrder,
WD=6 WaterShort)"] & I["FLG_OUT/TYP_OUT
(HMI: 0=Idle, 1-6=Typ)"] G --> J["K_ACCUM Instances
(6x per Typ 1-6:
Buffer(1): Temp
Agreed(2): Planned
Random(3): Unplanned
Elec(4): Power
NoOrder(5): Demand
Water(6): Supply)
(RETAIN Loop)"] E --> J K["RATE
(SFLOAT#1.0)"] --> J J --> L["ACC_OUT
(structKAccum per Typ)
(RETAIN Persistent)"] L --> S["ALT_OUT Total
(FLOAT per Typ 1-6)"] S --> T["OEE Report/Audit
(Chain ADD Typ 2-6
Availability Calc)"] H ---> O["K_ADD_ACCUM
(Merge Buffer + Typ Target)"] O ---> Q["K_SUB_ACCUM
(Reset Buffer = 0)"] J ---> O & Q Q ---> J A:::ioClass B:::fbClass C:::ioClass KOEE:::koeeClass E:::fbClass G:::stateClass H:::stateClass I:::ioClass J:::fbClass K:::fbClass L:::fbClass S:::fbClass T:::ioClass O:::fbClass Q:::fbClass classDef fbClass fill:#e1f5fe,stroke:#01579b,stroke-width:2px classDef koeeClass fill:#fff3e0,stroke:#ef6c00,stroke-width:2px classDef ioClass fill:#f3e5f5,stroke:#4a148c classDef stateClass fill:#e8f5e8,stroke:#1b5e20

Penutup

Buffer runtime mempertahankan durasi downtime sampai ditransfer ke kategori yang benar. Menunda klasifikasi berisiko mencampur kategori dan mengaburkan akar penyebab, sehingga klasifikasi cepat menjadi kunci akurasi dan auditabilitas OEE. Dirancang mengikuti realitas lapangan, logika ini mendukung pengambilan keputusan operator secara tepat dan manusiawi. Sebelum plant start, operator wajib memastikan kategori shutdown telah dipilih sesuai checklist untuk mencegah tercampurnya durasi antarkategori dan menjaga integritas data.

Manfaat utama:

  • Mengurangi risiko kehilangan runtime akibat kelengahan operator.
  • Audit trail kuat untuk penelusuran dan pemantauan performa plant.
  • Struktur kode modular dan portabel, mudah diadaptasi lintas DCS/PLC.