Published on

Production Reliability Design dengan C++

Authors

πŸ“˜ Artikel 7: Production Reliability Design dengan C++

Posisi: Artikel 7 dari 8 Domain Keputusan: Reliability & Error Model Status Lock: πŸ”’ Reliability & Error Handling Freeze Entry Context: IndustrialNode/IndustrialNode.ino



1. Problem Reality

Setelah Artikel 6:

  • Layering terkunci.
  • Allocation disiplin.
  • Concurrency terkendali.
  • Communication terisolasi.

Firmware terlihat matang.

Namun di lapangan muncul gejala:

  • β€œKadang mati sendiri.”
  • β€œKadang tidak kirim data.”
  • β€œKadang relay tidak sesuai status.”
  • Watchdog reset tanpa log jelas.
  • OTA gagal tanpa error code formal.

Masalah paling sering:

  • Error hanya di-print ke Serial.
  • Fungsi gagal tapi tidak dipropagasi.
  • Relay tetap ON saat sensor error (fail-open).
  • Tidak ada status sistem global yang eksplisit.

Masalahnya bukan arsitektur lagi.

Masalahnya:

Tidak ada model reliability formal.


2. Root Cause Analysis

Reliability rusak bukan karena satu bug, tetapi karena tidak ada struktur error.


2.1 Silent Failure

Contoh:

if (!sensor.read()) {
    Serial.println("Sensor error");
}

Function gagal, tetapi:

  • Tidak ada return status.
  • Tidak ada perubahan state.
  • Tidak ada fail-safe.

System berjalan seolah-olah normal.


2.2 Watchdog Tanpa Owner

esp_task_wdt_reset();   // dipanggil di banyak tempat - ❌

Watchdog di-feed dari banyak domain.

Jika deadlock terjadi di satu task:

  • Task lain masih feed watchdog.
  • Hang tidak terdeteksi.

2.3 Fail-Open Control

Jika sensor gagal:

  • Relay tetap ON.
  • Heater tetap aktif.
  • Motor tetap berputar.

Software terlihat β€œhidup”, hardware rusak perlahan.


2.4 Logging Tidak Terstruktur

Serial.println("WiFi error");
Serial.println("Temp fail");

Tidak ada:

  • Severity level
  • Error code
  • Aggregation
  • Telemetry integration

Ketika device di lapangan:

  • Tidak ada audit trail.

πŸ”Ž Visualisasi Error Tanpa Propagasi

Image

Image

Tanpa error model:

  • Failure tidak naik ke Application.
  • Fail-safe tidak pernah aktif.
  • System terlihat normal hingga terlambat.

3. Design Principle (Rule yang Dikunci)

Artikel 7 mengunci reliability baseline final.


πŸ”’ Rule 1 β€” Error Propagation via Explicit Status

Semua fungsi kritikal harus:

  • Return Status enum atau
  • Update error state internal

Contoh:

enum class Status {
    OK,
    SENSOR_ERROR,
    COMM_ERROR,
    STORAGE_ERROR,
    INTERNAL_ERROR
};

Tidak boleh:

  • ❌ Silent failure
  • ❌ Ignore return value

πŸ”’ Rule 2 β€” No Exception

Exception dilarang.

Alasan:

  • Stack tidak terprediksi.
  • Flow tersembunyi.
  • Tidak deterministik.

Error flow harus eksplisit.


πŸ”’ Rule 3 β€” Watchdog Encapsulated

Watchdog harus dikelola oleh satu class:

svc_HealthService

Tidak boleh di-feed sembarang tempat.


πŸ”’ Rule 4 β€” Fail-Safe State Mandatory

Jika error kritikal terjadi:

  • System masuk state FAILSAFE.
  • Relay ke default aman.
  • Control loop tetap berjalan dalam mode terbatas.

Tidak boleh fail-open.


πŸ”’ Rule 5 β€” Logging via Interface Only

Tidak boleh:

  • Serial.println liar di semua file.
  • Logging blocking di ControlTask.

Harus:

  • ILogger interface
  • Bisa redirect (Serial / MQTT / disable)
  • Non-blocking via queue

πŸ”’ Rule 6 β€” Health Telemetry Mandatory

Minimal metric:

  • reset_reason
  • heap_min_free
  • backlog_depth
  • wifi_state
  • error_code

Harus masuk telemetry.


Setelah Artikel 7:

  • ❌ Tidak boleh direct printf liar
  • ❌ Tidak boleh exception
  • ❌ Tidak boleh silent failure
  • ❌ Tidak boleh fail-open control

Reliability baseline final.


4. Implementation Pattern (ESP32 Arduino Context)


4.1 Error Enum Pattern

enum class Status {
    OK,
    SENSOR_ERROR,
    COMM_ERROR,
    STORAGE_ERROR,
    INTERNAL_ERROR
};

Service method:

Status SensorService::update() {
    if (!driver_.isReady()) {
        return Status::SENSOR_ERROR;
    }
    // read sensor
    return Status::OK;
}

Application layer memutuskan transisi state.


4.2 HealthService Skeleton

class HealthService {
public:
    void feedWatchdog();
    void reportError(Status s);
    Status currentError() const;

private:
    Status last_error_ = Status::OK;
};

Watchdog hanya di-feed di satu domain.


4.3 Fail-Safe Pattern

if (health.currentError() != Status::OK) {
    actuator.enterFailSafe();
}

Fail-safe eksplisit, bukan implicit.


4.4 Logging Abstraction

class ILogger {
public:
    virtual void log(LogLevel level, const char* msg) = 0;
};

ControlTask tidak boleh blocking log.


5. Constraint & Embedded Impact

Reliability bukan sekadar β€œtambahan fitur”. Ia harus selaras dengan constraint embedded yang sudah dikunci di Artikel 3–6.


5.1 RAM Impact

  • Status enum sangat kecil (biasanya 4 byte).
  • Health metric disimpan dalam struktur ringkas.
  • Logging queue harus bounded (fixed-size).

Jika logging menggunakan String atau buffer dinamis:

  • Heap churn.
  • Fragmentasi.
  • Deadlock saat memory rendah.

Reliability model harus ringan dan deterministic.


5.2 Flash Impact

Menambahkan:

  • Error enum
  • HealthService
  • Logging abstraction

Menambah flash sedikit.

Namun ini trade-off yang benar:

Auditability > sedikit penghematan flash.


5.3 Stack Impact

Tanpa exception:

  • Stack depth lebih predictable.
  • Tidak ada unwind chain tak terduga.

Logging via queue:

  • Mengurangi call stack di ControlTask.
  • Mencegah blocking di path kritikal.

5.4 Determinism Impact

Tanpa error model:

  • Fungsi bisa gagal diam-diam.
  • Control tetap berjalan dalam kondisi invalid.

Dengan explicit status:

  • Error naik ke Application.
  • Transisi state eksplisit.
  • Fail-safe bisa aktif tepat waktu.

Reliability meningkatkan determinism, bukan menguranginya.


6. Failure Scenario

Reliability rule diuji di kondisi terburuk.


Scenario 1 β€” Sensor Gagal, Relay Tetap ON

Tanpa error propagation:

sensor.update();

Return value diabaikan.

Relay tetap ON.

Potensi:

  • Overheat.
  • Kerusakan mekanikal.

Dengan rule:

Status s = sensor.update();
if (s != Status::OK) {
    health.reportError(s);
}

Application memicu fail-safe.

Relay kembali ke safe state.


Scenario 2 β€” Heap Hampir Habis

Tanpa health telemetry:

  • TLS gagal.
  • Publish gagal.
  • Tidak ada peringatan awal.

Dengan health metric:

  • heap_min_free dikirim ke cloud.
  • Bisa dideteksi sebelum crash.

Proaktif, bukan reaktif.


Scenario 3 β€” Deadlock tapi Watchdog Tetap Di-Feed

Jika watchdog di-feed di banyak tempat:

esp_task_wdt_reset();   // tersebar - ❌

Deadlock di satu task tidak terdeteksi.

Dengan encapsulation:

  • Hanya HealthService yang feed.
  • Jika ControlTask hang β†’ watchdog tidak di-feed.
  • Reset terjadi sesuai desain.

Scenario 4 β€” Logging Blocking di ControlTask

Jika log langsung:

Serial.println("Error");

Saat USB slow atau buffer penuh:

  • Blocking.
  • Watchdog reset.

Dengan logging via queue:

  • ControlTask hanya enqueue.
  • Logging diproses di task lain.

7. Anti-Pattern

Daftar merah Artikel 7:

  • ❌ Exception handling
  • ❌ Silent catch-all
  • ❌ Mengabaikan return status
  • ❌ Fail-open relay
  • ❌ Watchdog feed tersebar
  • ❌ Logging blocking di ControlTask
  • ❌ Tidak ada health telemetry

Dampak:

  • Tidak bisa audit root cause
  • Uptime rendah
  • Debug mahal
  • Device gagal di lapangan tanpa jejak

8. Freeze Point

Setelah Artikel 7, keputusan berikut dianggap final:

  • Error propagation eksplisit via Status.
  • Exception dilarang.
  • Watchdog encapsulated di svc_HealthService.
  • Fail-safe state mandatory.
  • Logging hanya via interface non-blocking.
  • Health telemetry minimal wajib dikirim.

Reliability architecture tidak boleh berubah.

Artikel 8 hanya mengaudit dan menjaga, bukan menambah rule baru.


9. Engineering Checklist

Audit sebelum release produksi:

  • Apakah semua fungsi kritikal return Status?
  • Apakah ada return value yang diabaikan?
  • Apakah relay punya default safe state?
  • Apakah watchdog di-feed di banyak tempat?
  • Apakah logging blocking?
  • Apakah health metric masuk telemetry?

Jika satu saja β€œya” β†’ melanggar Artikel 7.


10. Summary (5 Bullet Maksimal)

  • Firmware rapi belum tentu reliable.
  • Error harus eksplisit, bukan tersembunyi.
  • Fail-safe bukan opsional.
  • Watchdog harus punya owner.
  • Observability adalah bagian dari arsitektur.

Catatan Penyusunan Artikel ini disusun sebagai materi edukasi dan referensi umum berdasarkan berbagai sumber pustaka, praktik lapangan, serta bantuan alat penulisan. Pembaca disarankan untuk melakukan verifikasi lanjutan dan penyesuaian sesuai dengan kondisi serta kebutuhan masing-masing sistem.