From 426546f1ad3fd2fbeb52329d7a880fb962982133 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 14:54:50 +0700 Subject: [PATCH 01/17] Add Indonesian download CTA --- landing/partials/download-cta-id.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 landing/partials/download-cta-id.html diff --git a/landing/partials/download-cta-id.html b/landing/partials/download-cta-id.html new file mode 100644 index 000000000..79698f9f6 --- /dev/null +++ b/landing/partials/download-cta-id.html @@ -0,0 +1 @@ +
Mulai dengan ARSAS

Unduh ARSAS {{ARSAS_VERSION}} untuk Windows.

Gunakan installer untuk workstation normal, pilih portable ZIP untuk laptop pengujian terkontrol, dan verifikasi paket menggunakan checksum SHA-256 yang dipublikasikan.

From 611091b377a14de5b9d2637c1fd136e37e57e2cf Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 14:55:37 +0700 Subject: [PATCH 02/17] Add Indonesian MMS client page --- landing/templates/mms-client-iec61850.html | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 landing/templates/mms-client-iec61850.html diff --git a/landing/templates/mms-client-iec61850.html b/landing/templates/mms-client-iec61850.html new file mode 100644 index 000000000..5b1990bf8 --- /dev/null +++ b/landing/templates/mms-client-iec61850.html @@ -0,0 +1,26 @@ + + + + + MMS Client dan IED Browser IEC 61850 untuk Windows | ARSAS + + + + + + + + + {{> header}} +
+
MMS client IEC 61850 · IED browser live · Windows

Telusuri model server IEC 61850 langsung dari IED.

ARSAS membangun association MMS ke endpoint yang sudah disetujui, melakukan discovery model live, lalu menjaga value, quality, timestamp, DataSet, Report Control Block, control object dan diagnostik tetap berada di dalam konteks IED yang benar.

+
Discovery liveLogical Device, Logical Node, Data Object dan Data Attribute
Value bertipeValue, quality, timestamp dan tipe MMS yang ditemukan
Objek engineeringDataSet, BRCB, URCB dan control model
State per IEDAssociation, pilihan signal dan diagnostik tetap terisolasi
+
Workflow praktis

Dari alamat IP yang disetujui menuju evidence live yang dapat dicari.

Mulai dari perangkat dan service yang benar-benar tersedia tanpa harus membangun ulang konteks yang sama di beberapa aplikasi terpisah.

01

Tambahkan endpoint IED

Buat association MMS independen agar kegagalan service tetap dapat dikaitkan dengan perangkat asal.

02

Discovery model server

Enumerasi hierarchy logis, tipe data live, DataSet, report control dan descriptor control yang ditawarkan IED.

03

Cari dan pilih signal

Filter model besar dan pilih hanya point yang diperlukan untuk FAT, SAT, commissioning atau troubleshooting.

04

Amati evidence yang dapat ditelusuri

Pertahankan identity IED, quality, timestamp, perubahan terakhir dan sumber acquisition yang sudah diverifikasi.

+
Screenshot aplikasi nyata

Satu model browser, satu live-value workspace, satu konteks IED.

Periksa perilaku perangkat saat ini dan jangan hanya bergantung pada tree dari engineering file statis.

Workspace awal MMS client IEC 61850 ARSAS

01Mulai dari perangkat

Tambahkan endpoint yang dituju atau buka konteks SCL yang tersedia.

Live value IEC 61850 dengan quality dan timestamp di ARSAS

02Live value yang dapat dicari

Lihat perangkat, quality, timestamp, source dan konteks perubahan dalam satu tempat.

+
Berguna untuk

FAT, SAT, commissioning dan troubleshooting protokol

  • Konfirmasi IED menerima association MMS
  • Bandingkan object live dengan file SCL yang tersedia
  • Temukan DataSet dan RCB sebelum uji reporting
  • Periksa ctlModel sebelum uji control
  • Pisahkan masalah model, service dan komunikasi
Batas engineering

Koneksi MMS bukan bukti interoperabilitas lengkap.

Association yang berhasil belum membuktikan reporting, control, file service, GOOSE binding atau seluruh object model sudah benar. Validasi tersebut tetap harus dilakukan secara eksplisit sesuai prosedur proyek.

+
Workflow berikutnya

Lanjutkan ke monitoring berbasis report.

Periksa coverage DataSet, ownership BRCB dan URCB, trigger option serta fallback.

Kapabilitas terkait

Bandingkan model live dengan konteks SCL.

Jaga configured intent dan perilaku perangkat saat ini tetap terlihat bersama.

+ {{> download-cta-id}} +
+ {{> footer}} + + From 4dc31df229f625cf75919efa1dd9dd960044abd6 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 14:56:28 +0700 Subject: [PATCH 03/17] Add Indonesian Smart Reporting page --- .../templates/smart-reporting-iec61850.html | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 landing/templates/smart-reporting-iec61850.html diff --git a/landing/templates/smart-reporting-iec61850.html b/landing/templates/smart-reporting-iec61850.html new file mode 100644 index 000000000..cdd27dc95 --- /dev/null +++ b/landing/templates/smart-reporting-iec61850.html @@ -0,0 +1,26 @@ + + + + + Smart Reporting IEC 61850 — BRCB, URCB dan Fallback | ARSAS + + + + + + + + + {{> header}} +
+
BRCB · URCB · coverage DataSet · fallback yang terlihat

Pilih signal yang dibutuhkan. ARSAS membangun jalur acquisition yang dapat dipertanggungjawabkan.

Smart Reporting bukan janji bahwa semua value pasti datang melalui report. Workflow ini memulai MMS read, mengevaluasi report yang sudah dikonfigurasi, membuktikan coverage secara tepat, memulihkan gap tertentu bila diizinkan, dan mempertahankan polling untuk point yang masih belum tercakup atau belum terverifikasi.

+
Langsung aktifMMS read berjalan saat reporting dievaluasi
Configured firstDataSet, BRCB dan URCB existing diprioritaskan
Coverage tepatMember dan urutan divalidasi, bukan diasumsikan
Fallback dipertahankanPoint uncovered atau degraded tetap masuk bounded polling
+
Rencana acquisition per IED

Static report lebih dulu. Recovery spesifik untuk gap. Polling bila diperlukan.

Setiap keputusan tetap terikat pada association IED yang menghasilkan evidence, sehingga state satu perangkat tidak diam-diam mengubah perangkat lain.

01
Mulai MMS read terpilih

Tampilkan value awal, quality dan timestamp sambil memeriksa object report.

02
Discovery reporting existing

Baca DataSet, BRCB, URCB, reference, reservation state dan option yang relevan.

03
Validasi coverage secara tepat

Bandingkan signal terpilih dengan membership dan urutan DataSet aktual, bukan nama saja.

04
Pulihkan gap tertentu

Gunakan temporary DataSet dan RCB yang sesuai hanya saat IED serta prosedur proyek mengizinkan write.

05
Pertahankan fallback yang terlihat

Point rejected, uncovered, unverified atau degraded tetap berada dalam bounded polling.

Tidak ada report dead end.DataSet kosong, RCB occupied, write ditolak atau stream silent tidak harus menghentikan live-value workspace.Kembali ke MMS Client →
+
Masalah yang dibuat terlihat

Association berhasil belum membuktikan reporting berhasil.

Negative evidence tetap terlihat agar engineer dapat membedakan masalah association, configuration, ownership, trigger dan delivery.

DataSet kosong atau tidak lengkap

RCB dapat tersedia tetapi DataSet yang direferensikan kosong, tidak lengkap atau berbeda dari kebutuhan test.

RCB sudah reserved

Report dapat dimiliki client lain, reserved oleh konfigurasi atau tidak tersedia untuk association saat ini.

Coverage tidak sesuai

Nama DataSet yang terlihat benar bukan bukti setiap signal terpilih tersedia dalam urutan yang diperlukan.

GI berhasil, update spontan tidak ada

General interrogation dapat mengembalikan initial image sementara report perubahan tetap tidak muncul.

Dynamic recovery ditolak

IED dapat menolak write karena capability, access control, configuration atau ownership aktif.

Report stream degraded

Perubahan yang hilang, association loss atau delivery yang tidak valid dapat mengembalikan point ke polling.

+
Evidence yang dipertahankan

Setiap update tetap memiliki asal.

  • Konteks IED dan association
  • Value dan quality
  • Timestamp IED atau proses
  • Report reason bila tersedia
  • Sumber report, polling atau acquisition lain
  • Diagnostik coverage dan degradation
Batas engineering

Otomasi tidak menghapus persyaratan approval.

  • Configured report diprioritaskan sebelum write dipertimbangkan
  • Dynamic recovery bersifat conditional dan eksplisit
  • Polling adalah fallback yang sah, bukan kegagalan tersembunyi
  • Access IED dan kewenangan test tetap berada di luar aplikasi
  • Tidak ada klaim universal reporting interoperability
+
Live value multi-IED dengan evidence reporting di ARSAS

EvidenceWorkspace live value terpadu

Signal dari session IED independen tetap membawa device, value, quality, waktu dan sumber acquisition.

+ {{> download-cta-id}} +
+ {{> footer}} + + From eccca8b0739367d3c4d89b73a85d757b970e4e54 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 14:57:17 +0700 Subject: [PATCH 04/17] Add Indonesian GOOSE analyzer page --- .../templates/analyzer-goose-iec61850.html | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 landing/templates/analyzer-goose-iec61850.html diff --git a/landing/templates/analyzer-goose-iec61850.html b/landing/templates/analyzer-goose-iec61850.html new file mode 100644 index 000000000..94ca4b368 --- /dev/null +++ b/landing/templates/analyzer-goose-iec61850.html @@ -0,0 +1,26 @@ + + + + + Analyzer GOOSE IEC 61850 untuk Windows | ARSAS + + + + + + + + + {{> header}} +
+
Inspeksi GOOSE read-only · supervisi stream · model binding

Ubah traffic GOOSE IEC 61850 menjadi engineering evidence yang dapat ditelusuri.

ARSAS menangkap GOOSE melalui network interface yang disetujui dan menampilkan identity stream, sequence behavior, retransmission, Time Allowed to Live, flag, payload order serta konteks model yang tersedia tanpa mengirim frame GOOSE.

+
Identity streamAPPID, VLAN, destination MAC, goCBRef dan DataSet reference
SequencestNum, sqNum, ritme retransmission dan evidence perubahan state
SupervisiTAL, Test, ndsCom dan kesehatan stream yang terlihat
PayloadOrdered allData dengan konteks binding SCL atau live model
+
Workflow diagnostik

Tentukan apakah masalah berada pada traffic, sequence, konfigurasi atau interpretasi.

Daftar packet saja tidak cukup. ARSAS menyatukan evidence level stream agar engineer dapat membedakan traffic yang tidak ada, sequence stale, identity network salah, payload mismatch atau engineering context yang belum lengkap.

01

Pilih adapter yang disetujui

Gunakan interface yang terhubung ke segment station bus atau process bus yang dituju. Capture raw Ethernet membutuhkan Npcap.

02

Identifikasi setiap publisher stream

Kelompokkan frame berdasarkan APPID, destination MAC, VLAN, goCBRef dan DataSet evidence.

03

Periksa state dan sequence

Amati perubahan stNum, progression sqNum, retransmission dan TAL sebelum menilai publisher sehat.

04

Hubungkan payload dengan makna engineering

Bandingkan ordered allData dengan SCL atau live model, dan pertahankan uncertainty bila mapping belum lengkap.

+
Screenshot aplikasi nyata

Periksa detail stream tanpa kehilangan konteks IED yang lebih luas.

Workspace GOOSE berada di lingkungan proyek yang sama dengan MMS, SCL, reporting dan diagnostik.

Analyzer GOOSE IEC 61850 ARSAS dengan stream dan payload evidence

GOOSESupervisi stream dan inspeksi payload

Lihat publisher identity, APPID, VLAN, sequence, retransmission, TAL, flag, ordered payload dan model binding.

+
Pertanyaan penting

Apa yang dapat dibantu ARSAS untuk diverifikasi

  • Apakah publisher yang diharapkan terlihat pada interface terpilih?
  • Apakah stNum berubah saat source state berubah?
  • Apakah sqNum bergerak dengan pola retransmission yang masuk akal?
  • Apakah TAL diperbarui sebelum expiry?
  • Apakah APPID, VLAN, MAC, goCBRef dan DataSet sesuai desain?
  • Apakah payload dapat dipetakan ke engineering context?
Batas engineering

Capture evidence bukan hasil acceptance sistem proteksi.

ARSAS tidak mengirim GOOSE, membuktikan performa trip end-to-end atau mensertifikasi desain network. Protection logic, timing, redundancy, subscriber behavior dan acceptance akhir tetap membutuhkan prosedur yang disetujui.

+
Konteks terkait

Gunakan SCL untuk menjelaskan intended stream.

Bandingkan publisher identity dan payload order yang terlihat dengan configured model.

Evidence terkait

Jaga diagnostik komunikasi tetap attributable.

Pisahkan masalah MMS, GOOSE dan device-specific menggunakan session IED independen.

+ {{> download-cta-id}} +
+ {{> footer}} + + From e0365eb9e0de7b4d373930b6aa42af541ce4fbbd Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 14:58:04 +0700 Subject: [PATCH 05/17] Add Indonesian file transfer page --- .../transfer-file-comtrade-iec61850.html | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 landing/templates/transfer-file-comtrade-iec61850.html diff --git a/landing/templates/transfer-file-comtrade-iec61850.html b/landing/templates/transfer-file-comtrade-iec61850.html new file mode 100644 index 000000000..4b033ae21 --- /dev/null +++ b/landing/templates/transfer-file-comtrade-iec61850.html @@ -0,0 +1,27 @@ + + + + + Transfer File dan Download COMTRADE IEC 61850 | ARSAS + + + + + + + + + {{> header}} +
+
MMS file service · fault record relay · download COMTRADE

Ambil fault record relay proteksi di dalam konteks IED yang benar.

ARSAS memakai MMS file service IEC 61850 untuk memeriksa directory yang tersedia, menelusuri path secara terkontrol dan mengunduh disturbance record sambil mempertahankan endpoint owner, progress transfer serta negative service evidence.

+
Discovery servicePeriksa apakah IED menyediakan MMS file service yang dapat digunakan
Browsing terbatasTelusuri path yang disetujui tanpa recursive traversal tak terkendali
Evidence transferProgress, cancellation, timeout dan negative response tetap terlihat
Konteks recordKelompokkan file COMTRADE berdasarkan IED dan event asal
+
Workflow fault record

Dari event relay menuju record set yang dapat diunduh dan diperiksa.

Setiap vendor dapat memakai struktur directory dan pola nama yang berbeda. ARSAS membuat proses discovery terlihat dan tidak menganggap semua relay menyimpan file di lokasi yang sama.

01

Hubungkan relay yang dituju

Gunakan association MMS per IED agar operasi file tetap terkait dengan endpoint dan project context yang benar.

02

Probe dan browse path yang tersedia

Periksa directory dengan boundary eksplisit, cancellation dan service failure yang tetap terlihat.

03

Identifikasi disturbance record yang relevan

Tinjau filename, timestamp dan extension pasangan seperti CFG, DAT, HDR dan INF tanpa menyembunyikan record set yang tidak lengkap.

04

Unduh dan simpan evidence transfer

Pertahankan source path, destination, progress, retry boundary dan detail negative result untuk review berikutnya.

+
Workflow COMTRADE-aware

Perlakukan fault record sebagai satu set, bukan satu file saja.

Disturbance record yang berguna dapat terdiri dari configuration, sampled data, header dan information companion.

  • CFG untuk konfigurasi channel
  • DAT untuk sampled data
  • HDR bila relay menyediakan header deskriptif
  • INF atau companion information yang disetujui
Failure-aware engineering

Negative evidence merupakan bagian dari hasil.

Directory kosong, access rejection, timeout, unsupported service atau partial record set tidak boleh ditampilkan sebagai sukses.

  • Unsupported service tetap eksplisit
  • Timeout dan cancellation dapat dibedakan
  • Partial download tidak diterima diam-diam
  • Layout vendor-specific tidak dianggap universal
+
Diagnostik IEC 61850 ARSAS untuk workflow transfer file relay

EvidenceDiagnostik service yang attributable

Pisahkan association, service, perilaku relay dan masalah transfer dari generic download error.

+
Berguna untuk

Workflow proteksi dan commissioning

  • Ambil fault record setelah relay beroperasi
  • Kumpulkan disturbance evidence saat FAT atau SAT
  • Bandingkan ketersediaan record antar IED
  • Simpan file untuk software analisis COMTRADE
  • Investigasi record set yang hilang atau tidak lengkap
Batas engineering

File yang berhasil diunduh tetap harus divalidasi.

Transfer sukses belum membuktikan record lengkap, scaling benar atau layak untuk analisis proteksi. Periksa time basis, channel definition, record set dan konfigurasi relay sesuai prosedur.

+
Kapabilitas terkait

Konfirmasi identity relay melalui MMS lebih dulu.

+ {{> download-cta-id}} +
+ {{> footer}} + + From 2e80ddda390b67ae40ddaec4d6b54d2e3fbca3a6 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 14:58:53 +0700 Subject: [PATCH 06/17] Add Indonesian SCL workspace page --- landing/templates/workspace-scl-iec61850.html | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 landing/templates/workspace-scl-iec61850.html diff --git a/landing/templates/workspace-scl-iec61850.html b/landing/templates/workspace-scl-iec61850.html new file mode 100644 index 000000000..fcfd2273a --- /dev/null +++ b/landing/templates/workspace-scl-iec61850.html @@ -0,0 +1,27 @@ + + + + + Workspace SCL IEC 61850 dan Export RCB Terpilih | ARSAS + + + + + + + + + {{> header}} +
+
SCD · CID · ICD · IID · SSD · configured-to-live evidence

Gunakan SCL sebagai engineering intent, lalu bandingkan dengan evidence relay yang tersedia sekarang.

ARSAS membuka input SCL umum, mengambil endpoint dan model context yang berguna, menjaga configured information tetap berdampingan dengan session IED live, serta mendukung export selected-RCB terkontrol untuk baseline Edition 1 atau Edition 2.

+
Input umumSCD, CID, ICD, IID, SSD dan XML context yang kompatibel
Perbandingan liveConfigured intent dan server behavior terlihat bersama
Review RCBPeriksa reporting path available dan occupied sebelum export
Output edition-awareBaseline selected-RCB untuk Edition 1 atau Edition 2
+
Workflow configured-to-live

Jangan memperlakukan file SCL yang tersedia sebagai kebenaran tanpa pemeriksaan.

Engineering file dapat tidak lengkap, outdated atau dibuat untuk tahap integrasi yang berbeda. ARSAS memakainya sebagai configured context sambil mempertahankan apa yang benar-benar ditawarkan relay.

01

Buka sumber SCL yang tersedia

Load file proyek atau device yang disetujui dan ambil endpoint, reference, communication setting, DataSet dan reporting context.

02

Hubungkan IED yang dituju

Discovery live server model dan biarkan perbedaan tetap terlihat, bukan mengganti salah satu sumber secara diam-diam.

03

Review ketersediaan reporting

Periksa RCB existing, ownership dan verified DataSet member sebelum memilih path untuk integrasi SAS atau gateway.

04

Export baseline terkontrol

Pilih RCB dan target edition, pertahankan member yang sudah diverifikasi, lalu buat baseline tanpa blind XML deletion.

+
Evidence integrasi

Export SCL selected-RCB untuk legacy SAS dan gateway.

Pilih reporting path secara sengaja dan pertahankan evidence relay yang tersedia saat export.

Export SCL RCB terpilih dengan opsi IEC 61850 Edition 1 dan Edition 2

SCLPilih RCB dan target edition

Periksa RCB available dan occupied, pilih reporting path, pertahankan verified DataSet member dan export baseline yang dinormalisasi.

+
Tujuan export

Kurangi cleanup integrasi yang sebenarnya dapat dihindari.

  • Fokus pada reporting path yang dipilih
  • Pertahankan verified DataSet member
  • Buat target edition eksplisit
  • Hindari menghapus section XML tanpa memahami relationship
  • Jaga source dan live evidence tetap traceable
Yang tidak dibuktikan

Export bukan sertifikasi interoperabilitas end-to-end.

SAS atau gateway penerima tetap harus diuji terhadap schema, edition behavior, naming, DataSet handling dan konfigurasi report yang didukung. Satu file export tidak dijamin bekerja tanpa perubahan pada semua vendor.

+
Berguna untuk

Integrasi multi-vendor dan troubleshooting

  • Bandingkan CID atau ICD dengan live relay model
  • Investigasi member report yang hilang atau mismatch
  • Review RCB availability sebelum gateway mapping
  • Buat baseline Edition 1 untuk legacy integration
  • Pertahankan configured dan observed context saat FAT atau SAT
Prinsip engineering

File dan perangkat adalah sumber evidence yang saling melengkapi.

Gunakan SCL untuk memahami intended configuration dan IED live untuk memahami current behavior. Perbedaan adalah evidence yang memerlukan review, bukan otomatis fault pada relay atau file.

+
Kapabilitas terkait

Discovery live model melalui MMS.

Traffic terkait

Bandingkan configured GOOSE dengan observed stream.

+ {{> download-cta-id}} +
+ {{> footer}} + + From 97b0f77d3293cd339ce426721a6eb3852bf66d95 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 14:59:42 +0700 Subject: [PATCH 07/17] Add Indonesian FAT testing page --- landing/templates/pengujian-fat-iec61850.html | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 landing/templates/pengujian-fat-iec61850.html diff --git a/landing/templates/pengujian-fat-iec61850.html b/landing/templates/pengujian-fat-iec61850.html new file mode 100644 index 000000000..36a8aa5af --- /dev/null +++ b/landing/templates/pengujian-fat-iec61850.html @@ -0,0 +1,26 @@ + + + + + Software Pengujian FAT IEC 61850 untuk Proteksi dan SAS | ARSAS + + + + + + + + + {{> header}} +
+
Factory acceptance testing · proteksi · SAS · integrasi gateway

Pengujian FAT IEC 61850 dengan evidence live langsung dari IED.

Gunakan ARSAS untuk discovery server model, memilih signal sesuai test procedure, memvalidasi reporting dan GOOSE, memeriksa command completion serta memastikan fault record dapat diakses sebelum equipment meninggalkan factory.

+
MMSDiscovery live data model dan service context
ReportingValidasi BRCB, URCB dan fallback acquisition
GOOSEPeriksa identity, sequence, retransmission dan payload
EvidenceValue, quality, time, reason dan outcome tetap terlihat
+
Workflow yang disarankan

Dari akses endpoint menuju evidence yang dapat disaksikan customer.

Sesuaikan urutan dengan FAT procedure dan tanggung jawab proyek yang sudah disetujui.

01

Connect dan discovery

Tambahkan endpoint, bangun association MMS dan periksa hierarchy live, DataSet, RCB serta control model.

02

Pilih signal FAT

Pilih status, measurement, quality dan timestamp yang dibutuhkan test sheet tanpa mapping seluruh IED secara buta.

03

Validasi acquisition

Prioritaskan configured reporting, periksa membership DataSet secara tepat dan pertahankan coverage yang tidak tersedia tetap terlihat.

04

Periksa GOOSE dan control

Review identity serta sequence stream, lalu jalankan Direct atau SBO hanya bila diizinkan dengan hasil completion yang terlihat.

05

Konfirmasi file dan diagnostik

Probe fault record dan pertahankan outcome komunikasi, report, frame serta service bersama IED asal.

+
Live value IEC 61850 ARSAS untuk pengujian FAT

Validasi signal FAT yang terfokus

Review signal terpilih bersama device identity, quality, timestamp, acquisition source dan recent-change evidence selama witness test.

+
Berguna untuk

FAT proteksi dan substation automation.

  • Communication check relay dan bay controller
  • Perilaku BRCB dan URCB
  • Evidence publishing dan subscription GOOSE
  • Validasi Direct dan SBO control
  • Akses disturbance-record file
Batas engineering

Acceptance FAT tetap bersifat prosedural.

ARSAS tidak menggantikan calibrated injection equipment, pengujian fungsi proteksi, approved drawing, settings review, cybersecurity control, witness sign-off atau tanggung jawab vendor.

+
Workflow terkait

Lanjutkan dari factory ke site.

+ {{> download-cta-id}} +
+ {{> footer}} + + From 254c574d8d751e5a9c155d3be2abcd33f8cd43b5 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:00:24 +0700 Subject: [PATCH 08/17] Add Indonesian SAT testing page --- landing/templates/pengujian-sat-iec61850.html | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 landing/templates/pengujian-sat-iec61850.html diff --git a/landing/templates/pengujian-sat-iec61850.html b/landing/templates/pengujian-sat-iec61850.html new file mode 100644 index 000000000..00fce0de1 --- /dev/null +++ b/landing/templates/pengujian-sat-iec61850.html @@ -0,0 +1,26 @@ + + + + + Software Pengujian SAT IEC 61850 untuk Integrasi Gardu | ARSAS + + + + + + + + + {{> header}} +
+
Site acceptance testing · installed network · integrasi end-to-end

Pengujian SAT IEC 61850 untuk evidence sistem yang sudah terpasang.

Gunakan ARSAS untuk mengonfirmasi ulang komunikasi IED setelah instalasi, memvalidasi selected value dan report, memeriksa traffic GOOSE, menguji control path yang disetujui serta mengisolasi masalah integrasi yang hanya muncul di site.

+
EndpointKonfirmasi alamat terpasang dan association independen
ReportingValidasi ulang DataSet dan RCB di site
Station busPeriksa identity, sequence dan payload GOOSE
DiagnostikPisahkan masalah network, service dan level IED
+
Workflow yang disarankan

Konfirmasi apa yang berubah antara factory dan site.

Instalasi, addressing, gateway mapping, switch configuration dan final project file dapat menimbulkan perbedaan yang tidak terlihat saat FAT.

01

Verifikasi akses endpoint

Konfirmasi IP address, interface dan association MMS independen untuk perangkat yang sudah terpasang.

02

Bandingkan configured dan live context

Gunakan SCL sebagai project intent sambil memeriksa model dan service yang benar-benar ditawarkan IED.

03

Validasi report dan value

Konfirmasi coverage DataSet, state RCB, quality, timestamp dan reason-for-inclusion.

04

Periksa perilaku GOOSE

Bandingkan APPID, VLAN, destination MAC, sequence counter, retransmission dan ordered payload dengan site design.

05

Isolasi kegagalan integrasi

Gunakan diagnostik per IED untuk membedakan association, report, frame, gateway, file-service dan command-completion problem.

+
ARSAS memonitor beberapa IED IEC 61850 saat pengujian SAT

Session independen untuk perangkat terpasang

Model, monitoring, reporting, event, file dan diagnostik tetap dapat dikaitkan ke setiap IED saat sistem gardu diuji.

+
Berguna untuk

Validasi site end-to-end.

  • Connectivity relay dan bay controller terpasang
  • Investigasi signal path SCADA dan gateway
  • GOOSE subscription dan station-bus check
  • Evidence control completion dan feedback
  • Akses disturbance record
Batas engineering

SAT tetap merupakan aktivitas proyek terkontrol.

ARSAS tidak menggantikan approved site procedure, authorization keamanan network, injection test, wiring check, protection-setting verification, switching control atau customer acceptance record.

+
Workflow terkait

Lanjutkan ke commissioning dan integrasi.

+ {{> download-cta-id}} +
+ {{> footer}} + + From 2e7a23206134482354b6680421c2cada11c77932 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:01:04 +0700 Subject: [PATCH 09/17] Add Indonesian commissioning page --- landing/templates/commissioning-iec61850.html | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 landing/templates/commissioning-iec61850.html diff --git a/landing/templates/commissioning-iec61850.html b/landing/templates/commissioning-iec61850.html new file mode 100644 index 000000000..c6c9cc3d1 --- /dev/null +++ b/landing/templates/commissioning-iec61850.html @@ -0,0 +1,26 @@ + + + + + Software Commissioning IEC 61850 untuk Troubleshooting Live | ARSAS + + + + + + + + + {{> header}} +
+
Commissioning terkontrol · troubleshooting live · attributable evidence

Commissioning IEC 61850 dengan evidence live-system yang jelas.

Gunakan ARSAS untuk memeriksa current value, quality, timestamp, reporting state, GOOSE behavior, command completion, akses fault record serta diagnostik per IED ketika aktivitas commissioning dilakukan berdasarkan prosedur yang disetujui.

+
Live valueDevice, quality, timestamp dan acquisition source
SOESemantic event terkontrol dengan konteks IED asal
ControlOutcome Direct dan SBO beserta completion evidence
DiagnostikAssociation, service, report dan frame visibility
+
Workflow yang disarankan

Investigasi live path sebelum mengubah konfigurasi.

Tekanan commissioning sering mendorong perubahan terlalu cepat. Mulailah dengan mempertahankan evidence yang sudah tersedia dari IED dan network.

01

Tetapkan konteks perangkat

Konfirmasi endpoint, active interface, device identity dan live MMS model sebelum menginterpretasi hasil.

02

Amati value dan event

Review selected value, quality, process timestamp, reason-for-inclusion dan semantic event history.

03

Periksa reporting dan GOOSE

Periksa RCB state, coverage DataSet, stream identity, sequence counter, retransmission dan payload evidence.

04

Jalankan hanya operasi yang disetujui

Gunakan Direct atau SBO bertipe hanya saat procedure, role dan kondisi plant mengizinkan active control.

05

Pertahankan negative evidence

Unsupported service, rejected write, report gap, timeout dan command failure tetap terlihat untuk root-cause analysis.

+
Event log IEC 61850 ARSAS untuk troubleshooting commissioning

Event evidence dengan konteks perangkat

Pisahkan continuous value dari perubahan bermakna sambil mempertahankan timestamp, quality, source dan IED attribution.

+
Berguna untuk

Investigasi live-system yang terkontrol.

  • Troubleshooting reporting dan quality
  • Pemeriksaan GOOSE sequence dan subscription
  • Review control completion dan feedback
  • Pengambilan fault record setelah event
  • Diagnostik komunikasi multi-IED
Batas engineering

Kewenangan commissioning tetap berada di luar aplikasi.

ARSAS tidak memberikan switching permission, menentukan keselamatan plant, menggantikan protection testing, menyetujui setting atau meniadakan site procedure, cybersecurity control dan independent verification.

+
Workflow terkait

Hubungkan commissioning dengan lifecycle proyek.

+ {{> download-cta-id}} +
+ {{> footer}} + + From cf7974cce22cde090f4e0ea527d6f25725c64a8f Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:01:46 +0700 Subject: [PATCH 10/17] Add Indonesian multi-vendor integration page --- .../integrasi-multi-vendor-iec61850.html | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 landing/templates/integrasi-multi-vendor-iec61850.html diff --git a/landing/templates/integrasi-multi-vendor-iec61850.html b/landing/templates/integrasi-multi-vendor-iec61850.html new file mode 100644 index 000000000..938d97fd4 --- /dev/null +++ b/landing/templates/integrasi-multi-vendor-iec61850.html @@ -0,0 +1,26 @@ + + + + + Integrasi Multi-Vendor IEC 61850 dan Pengujian SCL | ARSAS + + + + + + + + + {{> header}} +
+
SCL · live model · reporting · GOOSE · integrasi gateway

Integrasi multi-vendor IEC 61850 dengan configured dan live evidence dalam satu konteks.

Gunakan ARSAS untuk membandingkan project SCL dengan service dan model yang ditawarkan setiap IED, memeriksa reporting serta GOOSE, lalu menyiapkan baseline selected-RCB terkontrol untuk integrasi SAS atau gateway.

+
Konteks SCLInput engineering SCD, CID, ICD, IID dan SSD
Live modelHierarchy, service, DataSet dan RCB yang benar-benar terlihat
GOOSEAPPID, VLAN, reference, sequence dan payload
ExportBaseline selected-RCB Edition 1 atau Edition 2
+
Workflow yang disarankan

Temukan mismatch sebelum mengedit proyek.

Masalah interoperability sering berasal dari perbedaan antara configured intent, live server behavior dan expectation SAS atau gateway penerima.

01

Buka konteks proyek

Load SCL dan identifikasi IED, access point, DataSet, RCB, GOOSE control block serta address yang dimaksud.

02

Discovery IED live

Connect ke endpoint yang disetujui dan periksa model, service, DataSet, report control dan command descriptor aktual.

03

Bandingkan reporting evidence

Periksa RCB availability, ownership, DataSet membership, report behavior serta operasi yang ditolak atau unsupported.

04

Periksa identity GOOSE

Verifikasi APPID, VLAN, destination MAC, goCBRef, DataSet reference, sequence counter dan ordered payload.

05

Siapkan baseline terkontrol

Pilih RCB yang dimaksud, pertahankan verified DataSet member dan export baseline SCL Edition 1 atau 2 untuk review berikutnya.

+
Export SCL RCB terpilih untuk integrasi multi-vendor IEC 61850

Export SCL selected-RCB

Review RCB availability, pilih reporting path dan pertahankan verified DataSet member sebelum export baseline integrasi.

+
Berguna untuk

Integrasi SAS, gateway dan IED.

  • Alignment proyek Edition 1 dan Edition 2
  • Troubleshooting DataSet dan RCB reporting
  • Verifikasi address serta payload GOOSE
  • Perbandingan configured-to-live model
  • Attribution diagnostik multi-IED
Batas engineering

Evidence bukan universal conformance.

Koneksi atau export yang berhasil belum membuktikan conformance penuh, interoperability dengan semua vendor, kebenaran proyek atau acceptance oleh receiving system. Final engineering review tetap diperlukan.

+
Area produk terkait

Periksa evidence di balik integrasi.

+ {{> download-cta-id}} +
+ {{> footer}} + + From e8f7b32a60c1b16207cc582127b928b58cd72019 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:04:13 +0700 Subject: [PATCH 11/17] Register Indonesian high-value pages --- landing/site.json | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/landing/site.json b/landing/site.json index 122d22ddc..9f837499a 100644 --- a/landing/site.json +++ b/landing/site.json @@ -29,10 +29,14 @@ {"path": "unduh.html", "template": "unduh.html", "language": "id", "contentType": "localized", "alternates": {"en": "download.html", "id": "unduh.html", "x-default": "download.html"}, "changefreq": "weekly", "priority": "0.95", "lastmod": "2026-07-20"}, {"path": "features.html", "template": "features.html", "changefreq": "monthly", "priority": "0.94", "lastmod": "2026-07-20"}, {"path": "solutions.html", "template": "solutions.html", "changefreq": "monthly", "priority": "0.96", "lastmod": "2026-07-20"}, - {"path": "fat-testing.html", "template": "fat-testing.html", "changefreq": "monthly", "priority": "0.94", "lastmod": "2026-07-20"}, - {"path": "sat-testing.html", "template": "sat-testing.html", "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-20"}, - {"path": "commissioning.html", "template": "commissioning.html", "changefreq": "monthly", "priority": "0.94", "lastmod": "2026-07-20"}, - {"path": "multi-vendor-integration.html", "template": "multi-vendor-integration.html", "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-20"}, + {"path": "fat-testing.html", "template": "fat-testing.html", "language": "en", "alternates": {"en": "fat-testing.html", "id": "pengujian-fat-iec61850.html", "x-default": "fat-testing.html"}, "changefreq": "monthly", "priority": "0.94", "lastmod": "2026-07-20"}, + {"path": "pengujian-fat-iec61850.html", "template": "pengujian-fat-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "fat-testing.html", "id": "pengujian-fat-iec61850.html", "x-default": "fat-testing.html"}, "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-07-20"}, + {"path": "sat-testing.html", "template": "sat-testing.html", "language": "en", "alternates": {"en": "sat-testing.html", "id": "pengujian-sat-iec61850.html", "x-default": "sat-testing.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-20"}, + {"path": "pengujian-sat-iec61850.html", "template": "pengujian-sat-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "sat-testing.html", "id": "pengujian-sat-iec61850.html", "x-default": "sat-testing.html"}, "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-20"}, + {"path": "commissioning.html", "template": "commissioning.html", "language": "en", "alternates": {"en": "commissioning.html", "id": "commissioning-iec61850.html", "x-default": "commissioning.html"}, "changefreq": "monthly", "priority": "0.94", "lastmod": "2026-07-20"}, + {"path": "commissioning-iec61850.html", "template": "commissioning-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "commissioning.html", "id": "commissioning-iec61850.html", "x-default": "commissioning.html"}, "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-07-20"}, + {"path": "multi-vendor-integration.html", "template": "multi-vendor-integration.html", "language": "en", "alternates": {"en": "multi-vendor-integration.html", "id": "integrasi-multi-vendor-iec61850.html", "x-default": "multi-vendor-integration.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-20"}, + {"path": "integrasi-multi-vendor-iec61850.html", "template": "integrasi-multi-vendor-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "multi-vendor-integration.html", "id": "integrasi-multi-vendor-iec61850.html", "x-default": "multi-vendor-integration.html"}, "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-20"}, {"path": "guides.html", "template": "guides.html", "language": "en", "contentType": "guide-hub", "alternates": {"en": "guides.html", "id": "panduan.html", "x-default": "guides.html"}, "changefreq": "weekly", "priority": "0.95", "lastmod": "2026-07-20"}, {"path": "panduan.html", "template": "panduan.html", "language": "id", "contentType": "localized", "alternates": {"en": "guides.html", "id": "panduan.html", "x-default": "guides.html"}, "changefreq": "weekly", "priority": "0.92", "lastmod": "2026-07-20"}, {"path": "reporting-silent.html", "template": "reporting-silent.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-20"}, @@ -46,11 +50,16 @@ {"path": "live-model-vs-scl.html", "template": "live-model-vs-scl.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.89", "lastmod": "2026-07-20"}, {"path": "direct-vs-sbo.html", "template": "direct-vs-sbo.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.87", "lastmod": "2026-07-20"}, {"path": "commandtermination-addcause.html", "template": "commandtermination-addcause.html", "contentType": "guide", "changefreq": "monthly", "priority": "0.87", "lastmod": "2026-07-20"}, - {"path": "mms-client.html", "template": "mms-client.html", "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-20"}, - {"path": "smart-reporting.html", "template": "smart-reporting.html", "changefreq": "monthly", "priority": "0.95", "lastmod": "2026-07-20"}, - {"path": "goose-analyzer.html", "template": "goose-analyzer.html", "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-07-20"}, - {"path": "file-transfer.html", "template": "file-transfer.html", "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-20"}, - {"path": "scl-workspace.html", "template": "scl-workspace.html", "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-20"}, + {"path": "mms-client.html", "template": "mms-client.html", "language": "en", "alternates": {"en": "mms-client.html", "id": "mms-client-iec61850.html", "x-default": "mms-client.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-20"}, + {"path": "mms-client-iec61850.html", "template": "mms-client-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "mms-client.html", "id": "mms-client-iec61850.html", "x-default": "mms-client.html"}, "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-20"}, + {"path": "smart-reporting.html", "template": "smart-reporting.html", "language": "en", "alternates": {"en": "smart-reporting.html", "id": "smart-reporting-iec61850.html", "x-default": "smart-reporting.html"}, "changefreq": "monthly", "priority": "0.95", "lastmod": "2026-07-20"}, + {"path": "smart-reporting-iec61850.html", "template": "smart-reporting-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "smart-reporting.html", "id": "smart-reporting-iec61850.html", "x-default": "smart-reporting.html"}, "changefreq": "monthly", "priority": "0.93", "lastmod": "2026-07-20"}, + {"path": "goose-analyzer.html", "template": "goose-analyzer.html", "language": "en", "alternates": {"en": "goose-analyzer.html", "id": "analyzer-goose-iec61850.html", "x-default": "goose-analyzer.html"}, "changefreq": "monthly", "priority": "0.92", "lastmod": "2026-07-20"}, + {"path": "analyzer-goose-iec61850.html", "template": "analyzer-goose-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "goose-analyzer.html", "id": "analyzer-goose-iec61850.html", "x-default": "goose-analyzer.html"}, "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-20"}, + {"path": "file-transfer.html", "template": "file-transfer.html", "language": "en", "alternates": {"en": "file-transfer.html", "id": "transfer-file-comtrade-iec61850.html", "x-default": "file-transfer.html"}, "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-20"}, + {"path": "transfer-file-comtrade-iec61850.html", "template": "transfer-file-comtrade-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "file-transfer.html", "id": "transfer-file-comtrade-iec61850.html", "x-default": "file-transfer.html"}, "changefreq": "monthly", "priority": "0.88", "lastmod": "2026-07-20"}, + {"path": "scl-workspace.html", "template": "scl-workspace.html", "language": "en", "alternates": {"en": "scl-workspace.html", "id": "workspace-scl-iec61850.html", "x-default": "scl-workspace.html"}, "changefreq": "monthly", "priority": "0.91", "lastmod": "2026-07-20"}, + {"path": "workspace-scl-iec61850.html", "template": "workspace-scl-iec61850.html", "language": "id", "contentType": "localized", "alternates": {"en": "scl-workspace.html", "id": "workspace-scl-iec61850.html", "x-default": "scl-workspace.html"}, "changefreq": "monthly", "priority": "0.89", "lastmod": "2026-07-20"}, {"path": "control.html", "template": "control.html", "changefreq": "monthly", "priority": "0.90", "lastmod": "2026-07-20"}, {"path": "multi-ied-monitoring.html", "template": "multi-ied-monitoring.html", "changefreq": "monthly", "priority": "0.89", "lastmod": "2026-07-20"}, {"path": "architecture.html", "template": "architecture.html", "changefreq": "monthly", "priority": "0.82", "lastmod": "2026-07-20"}, From 6ed1cea1f65efa505f591170bc808175170d6752 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:05:09 +0700 Subject: [PATCH 12/17] Expand Indonesian product hub --- landing/templates/id.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/landing/templates/id.html b/landing/templates/id.html index 56c54794a..ef54364d8 100644 --- a/landing/templates/id.html +++ b/landing/templates/id.html @@ -4,7 +4,7 @@ Software Pengujian IEC 61850 untuk Windows | ARSAS - + @@ -20,7 +20,7 @@ - + @@ -32,15 +32,17 @@ {{> header}}
-
Gratis dan open source · Windows · versi {{ARSAS_VERSION}}

Pengujian IEC 61850
tanpa setup yang berbelit.

Masukkan alamat IED yang sudah disetujui, discovery model MMS secara live, pilih signal yang dibutuhkan, lalu mulai validasi. ARSAS menggabungkan reporting, GOOSE, file fault record, SCL, diagnostik dan guarded control dalam satu workspace.

Tanpa akun atau license keyInstaller dan portable ZIPSource serta checksum publik
Tampilan awal workstation ARSAS untuk pengujian IEC 61850
+
Gratis dan open source · Windows · versi {{ARSAS_VERSION}}

Pengujian IEC 61850
tanpa setup yang berbelit.

Masukkan alamat IED yang sudah disetujui, discovery model MMS secara live, pilih signal yang dibutuhkan, lalu mulai validasi. ARSAS menggabungkan reporting, GOOSE, fault record, SCL, diagnostik dan guarded control dalam satu workspace.

Tanpa akun atau license keyInstaller dan portable ZIPSource serta checksum publik
Tampilan awal workstation ARSAS untuk pengujian IEC 61850
MMS + ReportingDiscovery live, BRCB, URCB, recovery dan polling yang terlihat
GOOSE + FileSupervisi stream dan pengambilan record terkait COMTRADE
SCL + IntegrasiPerbandingan konfigurasi dengan model live dan export RCB terpilih
Control + EvidenceDirect dan SBO dengan hasil command yang dapat ditelusuri
-
Untuk pekerjaan lapangan nyata

Mulai dari evidence yang tersedia, bukan dari asumsi konfigurasi.

ARSAS membantu engineer mempercepat FAT, SAT, commissioning dan troubleshooting dengan tetap memperlihatkan sumber data, kualitas, timestamp, reason, fallback dan batas kemampuan device.

FAT

Validasi sebelum pengiriman

Periksa model, signal, report, GOOSE, control outcome dan akses fault record sebelum panel meninggalkan factory.

SAT

Konfirmasi setelah instalasi

Uji ulang endpoint, reporting, station bus dan integrasi sesudah instalasi jaringan dan device di site.

Commissioning

Evidence saat sistem hidup

Telusuri live value, sequence of events, kualitas komunikasi dan penyelesaian command secara terarah.

Integrasi

Bandingkan SCL dengan model live

Gunakan konfigurasi sebagai intent, lalu konfirmasi reporting, GOOSE dan model yang benar-benar ditawarkan IED.

+
Kapabilitas utama

Mulai dari protokol dan evidence yang sedang dibutuhkan.

Lima halaman teknis utama sekarang tersedia penuh dalam Bahasa Indonesia dengan istilah IEC 61850 tetap dipertahankan agar tidak mengubah arti engineering.

MMS

MMS Client dan IED Browser

Discovery model live, pencarian signal, quality, timestamp, DataSet, RCB dan control object.

Reporting

Smart Reporting

Validasi BRCB, URCB dan coverage DataSet dengan recovery terkontrol serta polling fallback.

GOOSE

Analyzer GOOSE

Periksa APPID, VLAN, stNum, sqNum, TAL, retransmission dan ordered payload.

Fault record

Transfer File dan COMTRADE

Browse MMS file service, unduh CFG/DAT/HDR/INF dan pertahankan negative service evidence.

SCL

Workspace SCL

Bandingkan configured intent dengan live model serta export baseline selected-RCB Edition 1 atau 2.

-
Panduan Bahasa Indonesia

Mulai dari gejala yang sedang terjadi.

Halaman panduan Indonesia mengarahkan engineer ke masalah reporting silent, koneksi port 102, GOOSE sequence, COMTRADE, CID/SCL dan control.

Catatan bahasa

Detail teknis tertentu masih tersedia dalam bahasa Inggris.

Entry point utama sudah diterjemahkan penuh. Beberapa artikel teknis mendalam tetap diarahkan ke versi Inggris agar tidak menerbitkan terjemahan tipis atau tidak akurat.

+
Untuk pekerjaan proyek nyata

Gunakan ARSAS sepanjang lifecycle pengujian dan integrasi.

Workflow Indonesia menjaga evidence, batas kewenangan dan tanggung jawab engineering tetap eksplisit.

FAT

Validasi sebelum pengiriman

Periksa model, signal, report, GOOSE, control outcome dan akses fault record sebelum panel meninggalkan factory.

SAT

Konfirmasi setelah instalasi

Uji ulang endpoint, reporting, station bus dan integrasi sesudah instalasi jaringan dan perangkat di site.

Commissioning

Evidence saat sistem hidup

Telusuri live value, sequence of events, kualitas komunikasi dan penyelesaian command secara terarah.

Integrasi

Multi-vendor IEC 61850

Gunakan SCL sebagai intent, lalu konfirmasi reporting, GOOSE dan model yang benar-benar ditawarkan IED.

- {{> download-cta}} +
Panduan troubleshooting

Mulai dari gejala yang sedang terjadi.

Telusuri reporting silent, koneksi port 102, GOOSE sequence, COMTRADE, CID/SCL dan control berdasarkan evidence yang tersedia.

Technical review

Ketahui batas setiap klaim dan hasil.

Bedakan kemampuan available, conditional, preview dan roadmap serta gunakan evidence ARSAS di dalam prosedur engineering yang disetujui.

+ + {{> download-cta-id}}
{{> footer}} From 8f14757ae28b5c1d8b88b574cecdb0059785931d Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:06:02 +0700 Subject: [PATCH 13/17] Connect Indonesian guides to localized workflows --- landing/templates/panduan.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/landing/templates/panduan.html b/landing/templates/panduan.html index 8feadbb0a..5a7b12e31 100644 --- a/landing/templates/panduan.html +++ b/landing/templates/panduan.html @@ -38,9 +38,11 @@
SCL dan control

Bandingkan intent dengan behavior aktual.

CID atau SCL ditolak

Periksa struktur, namespace, edition, model reference dan kebutuhan vendor sebelum menyebut interoperability failure.

Buka technical guide →

Live model berbeda dari SCL

Gunakan SCL sebagai configured intent dan model live sebagai evidence server yang sedang aktif.

Buka technical guide →

CommandTermination dan AddCause

Positive operate response belum selalu berarti proses control sudah selesai.

Buka technical guide →
+
Workflow produk dalam Bahasa Indonesia

Lanjutkan diagnosis dengan konteks fitur yang tepat.

MMS Client

Periksa association, live model, DataSet, RCB dan control object dari IED.

Buka MMS Client →
+
Batas engineering

Gunakan evidence untuk mempersempit penyebab.

Kesimpulan akhir tetap bergantung pada approved design, vendor documentation, test procedure, network access dan independent verification.

- {{> download-cta}} + {{> download-cta-id}} {{> footer}} From 4ca69796a275b83db876456c575e753c841f46eb Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:06:36 +0700 Subject: [PATCH 14/17] Use Indonesian download CTA --- landing/templates/unduh.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/landing/templates/unduh.html b/landing/templates/unduh.html index 682447d18..c85136026 100644 --- a/landing/templates/unduh.html +++ b/landing/templates/unduh.html @@ -36,9 +36,9 @@
Verifikasi file

Periksa checksum sebelum digunakan di lingkungan engineering.

Checksum membantu memastikan package yang diunduh sama dengan file yang dipublikasikan pada release resmi.

SHA-256 checksums

Unduh daftar checksum resmi, lalu bandingkan dengan hasil hash lokal.

PowerShell

Get-FileHash .\ARSAS-Windows-x64-Setup.exe -Algorithm SHA256

Nilai hash harus identik dengan entry installer pada file checksum resmi.

-
Sebelum menjalankan

Gunakan dalam boundary yang disetujui.

Operating system

Gunakan Windows 10 atau Windows 11 x64 dengan hak akses yang sesuai untuk capture, file dan komunikasi yang dibutuhkan.

Network access

Pastikan endpoint, TCP port 102, adapter station bus dan firewall sudah diizinkan oleh project atau laboratory procedure.

Control safety

Jangan mengirim command ke primary equipment tanpa approved test boundary, interlock review dan personel yang bertanggung jawab.

Current scope

Baca capability, roadmap dan technical review policy untuk membedakan fitur available, conditional, preview dan roadmap.

+
Sebelum menjalankan

Gunakan dalam boundary yang disetujui.

Operating system

Gunakan Windows 10 atau Windows 11 x64 dengan hak akses yang sesuai untuk capture, file dan komunikasi yang dibutuhkan.

Network access

Pastikan endpoint, TCP port 102, adapter station bus dan firewall sudah diizinkan oleh project atau laboratory procedure.

Control safety

Jangan mengirim command ke primary equipment tanpa approved test boundary, interlock review dan personel yang bertanggung jawab.

Current scope

Baca capability, roadmap dan technical review policy untuk membedakan fitur available, conditional, preview dan roadmap.

- {{> download-cta}} + {{> download-cta-id}} {{> footer}} From 7dbcdb81c63763c7edd6c596810b96c99488e374 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:08:37 +0700 Subject: [PATCH 15/17] Generate page-level language alternates --- scripts/build-product-site.py | 56 ++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/scripts/build-product-site.py b/scripts/build-product-site.py index c108d6d12..a66db3b29 100644 --- a/scripts/build-product-site.py +++ b/scripts/build-product-site.py @@ -72,8 +72,10 @@ def read_config() -> dict[str, object]: raise SystemExit(f"landing/site.json has invalid {dotted}") if not isinstance(config.get("pages"), list) or not config["pages"]: raise SystemExit("landing/site.json must define a non-empty pages registry") - key_file = SOURCE / str(config["indexNow"]["keyFile"]) - if not key_file.exists() or key_file.read_text(encoding="utf-8").strip() != config["indexNow"]["key"]: + index_now = config["indexNow"] + assert isinstance(index_now, dict) + key_file = SOURCE / str(index_now["keyFile"]) + if not key_file.exists() or key_file.read_text(encoding="utf-8").strip() != index_now["key"]: raise SystemExit("IndexNow key metadata does not match the hosted key file") return config @@ -82,6 +84,7 @@ def token_values(config: dict[str, object], version: str) -> dict[str, str]: product = config["product"] author = config["author"] downloads = config["downloads"] + assert isinstance(product, dict) and isinstance(author, dict) and isinstance(downloads, dict) return { "ARSAS_VERSION": version, "PRODUCT_NAME": str(product["name"]), @@ -106,6 +109,7 @@ def replace(match: re.Match[str]) -> str: if not path.exists(): raise SystemExit(f"Missing landing partial: {path.relative_to(ROOT)}") return expand_partials(path.read_text(encoding="utf-8"), (*stack, name)) + previous = None while previous != text: previous = text @@ -157,7 +161,7 @@ def page_registry(config: dict[str, object]) -> list[dict[str, object]]: raise SystemExit(f"Registered landing template is missing: {template}") alternates = entry.get("alternates") if alternates is not None: - if not isinstance(alternates, dict) or not {"en", "id", "x-default"}.issubset(alternates): + if not isinstance(alternates, dict) or set(alternates) != {"en", "id", "x-default"}: raise SystemExit(f"Localized page has invalid alternates: {path or 'index.html'}") if path not in alternates.values(): raise SystemExit(f"Localized page alternates must reference itself: {path or 'index.html'}") @@ -167,6 +171,23 @@ def page_registry(config: dict[str, object]) -> list[dict[str, object]]: return normalized +def absolute_url(root: str, path: str) -> str: + return root if path == "" else root + path + + +def inject_alternate_links(text: str, entry: dict[str, object], root: str) -> str: + alternates = entry.get("alternates") + if not isinstance(alternates, dict): + return text + links = "\n".join( + f' ' + for language in ("en", "id", "x-default") + ) + if "" not in text: + raise SystemExit(f"Cannot inject alternate-language links into {entry.get('template')}") + return text.replace("", links + "\n", 1) + + def install_icon(output: Path, icon_size: str) -> None: destination = output / "assets" / "app-icon.png" destination.parent.mkdir(parents=True, exist_ok=True) @@ -177,12 +198,10 @@ def install_icon(output: Path, icon_size: str) -> None: manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") -def absolute_url(root: str, path: str) -> str: - return root if path == "" else root + path - - def write_sitemap(output: Path, config: dict[str, object], pages: list[dict[str, object]]) -> None: - root = str(config["product"]["canonicalRoot"]) + product = config["product"] + assert isinstance(product, dict) + root = str(product["canonicalRoot"]) has_alternates = any(isinstance(entry.get("alternates"), dict) for entry in pages) namespace = ' xmlns:xhtml="http://www.w3.org/1999/xhtml"' if has_alternates else "" lines = ['', f''] @@ -207,16 +226,19 @@ def write_sitemap(output: Path, config: dict[str, object], pages: list[dict[str, def write_build_info(output: Path, config: dict[str, object], version: str, pages: list[dict[str, object]]) -> None: + product = config["product"] + index_now = config["indexNow"] + assert isinstance(product, dict) and isinstance(index_now, dict) languages = sorted({str(entry.get("language", "en")) for entry in pages if entry.get("index", True) is not False}) payload = { "schemaVersion": 3, - "product": config["product"]["name"], + "product": product["name"], "version": version, - "canonicalRoot": config["product"]["canonicalRoot"], - "repository": config["product"]["repository"], + "canonicalRoot": product["canonicalRoot"], + "repository": product["repository"], "author": config["author"], "languages": languages, - "indexNowKeyLocation": str(config["product"]["canonicalRoot"]) + str(config["indexNow"]["keyFile"]), + "indexNowKeyLocation": str(product["canonicalRoot"]) + str(index_now["keyFile"]), "pages": [entry["path"] or "index.html" for entry in pages], } (output / "build-info.json").write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") @@ -233,6 +255,10 @@ def build(output: Path) -> None: pages = page_registry(config) width, height = icon_dimensions() icon_size = f"{width}x{height}" + product = config["product"] + index_now = config["indexNow"] + assert isinstance(product, dict) and isinstance(index_now, dict) + root = str(product["canonicalRoot"]) if output.exists(): shutil.rmtree(output) @@ -246,7 +272,9 @@ def build(output: Path) -> None: target = output / target_name target.parent.mkdir(parents=True, exist_ok=True) source_text = (TEMPLATES / str(entry["template"])).read_text(encoding="utf-8") - target.write_text(render(source_text, values, icon_size), encoding="utf-8") + rendered = render(source_text, values, icon_size) + rendered = inject_alternate_links(rendered, entry, root) + target.write_text(rendered, encoding="utf-8") generated.add(target_name) source_html = legacy_html_names() @@ -258,7 +286,7 @@ def build(output: Path) -> None: write_build_info(output, config, version, pages) required = { - *generated, "site.json", "sitemap.xml", "build-info.json", str(config["indexNow"]["keyFile"]), + *generated, "site.json", "sitemap.xml", "build-info.json", str(index_now["keyFile"]), "assets/app-icon.png", "assets/social-card.png", "assets/screenshots/arsas-first-launch.webp", "assets/screenshots/arsas-multi-ied.webp", "assets/screenshots/arsas-live-values.webp", "assets/screenshots/arsas-event-log.webp", From 55d3d12bd042038af1f7b36025ef4d1c4e049b53 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:10:32 +0700 Subject: [PATCH 16/17] Validate Indonesian localization phase two --- scripts/validate-product-source.py | 80 ++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/scripts/validate-product-source.py b/scripts/validate-product-source.py index 5a523c93f..3e86d7edd 100644 --- a/scripts/validate-product-source.py +++ b/scripts/validate-product-source.py @@ -30,7 +30,21 @@ "goose-sequence.html", "cid-rejected.html", "live-model-vs-scl.html", "direct-vs-sbo.html", "commandtermination-addcause.html", } -LOCALIZED_FILES = {"id.html", "panduan.html", "unduh.html"} +LOCALIZED_PAIRS = { + "": "id.html", + "download.html": "unduh.html", + "guides.html": "panduan.html", + "mms-client.html": "mms-client-iec61850.html", + "smart-reporting.html": "smart-reporting-iec61850.html", + "goose-analyzer.html": "analyzer-goose-iec61850.html", + "file-transfer.html": "transfer-file-comtrade-iec61850.html", + "scl-workspace.html": "workspace-scl-iec61850.html", + "fat-testing.html": "pengujian-fat-iec61850.html", + "sat-testing.html": "pengujian-sat-iec61850.html", + "commissioning.html": "commissioning-iec61850.html", + "multi-vendor-integration.html": "integrasi-multi-vendor-iec61850.html", +} +LOCALIZED_FILES = set(LOCALIZED_PAIRS.values()) FORBIDDEN_COPY = ( "without navigating source code", "without navigating the source repository", "without requiring repository navigation", "the website is the product front door", @@ -96,6 +110,7 @@ def replace(match: re.Match[str]) -> str: errors.append(f"missing partial: {path.relative_to(ROOT)}") return "" return expand_partials(path.read_text(encoding="utf-8"), errors, (*stack, name)) + previous = None while previous != text: previous = text @@ -149,12 +164,17 @@ def validate_template(path: Path, content_type: str | None, language: str | None for phrase in FORBIDDEN_COPY: if phrase in lowered: errors.append(f"{label}: contains internal-strategy copy: {phrase}") + if path.name != "404.html": for include in ("{{> header}}", "{{> footer}}"): if include not in raw: errors.append(f"{label}: missing shared include {include}") - if path.name not in ("index.html", "download.html", "404.html") and "{{> download-cta}}" not in raw: + if content_type == "localized": + if "{{> download-cta-id}}" not in raw: + errors.append(f"{label}: Indonesian page must use the shared Indonesian download CTA") + elif path.name not in ("index.html", "download.html", "404.html") and "{{> download-cta}}" not in raw: errors.append(f"{label}: missing shared download CTA") + if content_type == "guide": if '"@type":"TechArticle"' not in raw.replace(" ", ""): errors.append(f"{label}: guide must use TechArticle structured data") @@ -216,6 +236,7 @@ def validate_registry(config: dict[str, object], errors: list[str]) -> list[tupl return [] paths: set[str] = set() names: set[str] = set() + entries: dict[str, dict[str, object]] = {} templates: list[tuple[Path, str | None, str | None]] = [] guide_count = localized_count = authority_count = 0 for entry in pages: @@ -233,6 +254,7 @@ def validate_registry(config: dict[str, object], errors: list[str]) -> list[tupl errors.append(f"landing/site.json: duplicate template {template}") paths.add(page_path) names.add(template) + entries[page_path] = entry template_path = TEMPLATES / template templates.append((template_path, content_type if isinstance(content_type, str) else None, language if isinstance(language, str) else None)) if not template_path.exists(): @@ -253,14 +275,27 @@ def validate_registry(config: dict[str, object], errors: list[str]) -> list[tupl localized_count += 1 elif content_type == "authority": authority_count += 1 - if len(pages) != 35: - errors.append(f"landing/site.json: expected 35 pages, found {len(pages)}") + + if len(pages) != 44: + errors.append(f"landing/site.json: expected 44 pages, found {len(pages)}") if guide_count != 11: errors.append(f"landing/site.json: expected 11 troubleshooting guides, found {guide_count}") - if localized_count != 3: - errors.append(f"landing/site.json: expected 3 Indonesian pages, found {localized_count}") + if localized_count != 12: + errors.append(f"landing/site.json: expected 12 Indonesian pages, found {localized_count}") if authority_count != 1: errors.append(f"landing/site.json: expected one authority page, found {authority_count}") + + for english, indonesian in LOCALIZED_PAIRS.items(): + expected = {"en": english, "id": indonesian, "x-default": english} + en_entry, id_entry = entries.get(english), entries.get(indonesian) + if en_entry is None or id_entry is None: + errors.append(f"landing/site.json: missing localized pair {english or 'index.html'} / {indonesian}") + continue + if en_entry.get("language") != "en" or en_entry.get("alternates") != expected: + errors.append(f"landing/site.json: invalid English alternate contract for {english or 'index.html'}") + if id_entry.get("language") != "id" or id_entry.get("contentType") != "localized" or id_entry.get("alternates") != expected: + errors.append(f"landing/site.json: invalid Indonesian alternate contract for {indonesian}") + actual = {path.name for path in TEMPLATES.glob("*.html")} if actual - names: errors.append("templates missing from registry: " + ", ".join(sorted(actual - names))) @@ -273,8 +308,9 @@ def validate_partials(errors: list[str]) -> None: def read(name: str) -> str: path = PARTIALS / name return path.read_text(encoding="utf-8") if path.exists() else "" + header, footer = read("header.html"), read("footer.html") - cta, boundary = read("download-cta.html"), read("guide-boundary.html") + cta, cta_id, boundary = read("download-cta.html"), read("download-cta-id.html"), read("guide-boundary.html") for page in EXPECTED_NAV: if f'data-nav-page="{page}"' not in header: errors.append(f"shared header missing navigation key {page}") @@ -291,6 +327,9 @@ def read(name: str) -> str: for value in ("{{ARSAS_VERSION}}", "{{INSTALLER_URL}}", 'href="download.html"'): if value not in cta: errors.append(f"shared download CTA missing {value}") + for value in ("{{ARSAS_VERSION}}", "{{INSTALLER_URL}}", 'href="unduh.html"', "Unduh installer"): + if value not in cta_id: + errors.append(f"shared Indonesian download CTA missing {value}") for value in ("Ari Sulistiono", 'href="guides.html"', 'href="technical-review.html"', "Engineering boundary"): if value not in boundary: errors.append(f"shared guide boundary missing {value}") @@ -300,6 +339,7 @@ def validate_contract(errors: list[str]) -> None: def template(name: str) -> str: path = TEMPLATES / name return path.read_text(encoding="utf-8") if path.exists() else "" + home, download, about = template("index.html"), template("download.html"), template("about.html") solutions, guides = template("solutions.html"), template("guides.html") review, localized_home = template("technical-review.html"), template("id.html") @@ -325,13 +365,33 @@ def template(name: str) -> str: for value in ("Pengujian IEC 61850", 'href="panduan.html"', 'href="unduh.html"', 'hreflang="en"'): if value not in localized_home: errors.append(f"Indonesian homepage missing {value}") - for value in ("Panduan Troubleshooting", 'href="reporting-silent.html"', 'href="technical-review.html"'): + for localized in LOCALIZED_FILES - {"id.html", "panduan.html", "unduh.html"}: + if f'href="{localized}"' not in localized_home and f'href="{localized}"' not in localized_guides: + errors.append(f"Indonesian hubs do not link to {localized}") + for value in ("Panduan Troubleshooting", 'href="reporting-silent.html"', 'href="technical-review.html"', 'href="mms-client-iec61850.html"'): if value not in localized_guides: errors.append(f"Indonesian guide hub missing {value}") - for value in ("{{INSTALLER_URL}}", "{{PORTABLE_URL}}", "{{CHECKSUMS_URL}}", "Unduh ARSAS"): + for value in ("{{INSTALLER_URL}}", "{{PORTABLE_URL}}", "{{CHECKSUMS_URL}}", "Unduh ARSAS", "{{> download-cta-id}}"): if value not in localized_download: errors.append(f"Indonesian download page missing {value}") + localized_contracts = { + "mms-client-iec61850.html": ("MMS Client", "DataSet", "mms-client.html"), + "smart-reporting-iec61850.html": ("BRCB", "URCB", "smart-reporting.html"), + "analyzer-goose-iec61850.html": ("stNum", "sqNum", "goose-analyzer.html"), + "transfer-file-comtrade-iec61850.html": ("COMTRADE", "CFG", "file-transfer.html"), + "workspace-scl-iec61850.html": ("Edition 1", "selected-RCB", "scl-workspace.html"), + "pengujian-fat-iec61850.html": ("Pengujian FAT", "GOOSE", "fat-testing.html"), + "pengujian-sat-iec61850.html": ("Pengujian SAT", "station", "sat-testing.html"), + "commissioning-iec61850.html": ("Commissioning", "SOE", "commissioning.html"), + "integrasi-multi-vendor-iec61850.html": ("multi-vendor", "selected-RCB", "multi-vendor-integration.html"), + } + for name, required_values in localized_contracts.items(): + text = template(name) + for value in (*required_values, 'hreflang="en"', '"inLanguage":"id"', "{{> download-cta-id}}"): + if value not in text: + errors.append(f"{name}: missing localized contract {value}") + root_html = sorted(path.name for path in LANDING.glob("*.html") if not VERIFICATION_PATTERN.fullmatch(path.name)) if root_html: errors.append("legacy landing HTML remains outside templates: " + ", ".join(root_html)) @@ -374,7 +434,7 @@ def main() -> int: print(f"- {error}", file=sys.stderr) return 1 width, height = png_size(APP_ICON_SOURCE) - print(f"ARSAS product-source validation passed: {len(templates)} templates, 11 guides, 3 Indonesian pages, authority policy, IndexNow and {width}x{height} app icon.") + print(f"ARSAS product-source validation passed: {len(templates)} templates, 11 guides, 12 Indonesian pages and latest {width}x{height} app icon.") return 0 From 4e983a289c7a89f849689f22afe688d244718ee9 Mon Sep 17 00:00:00 2001 From: masarray Date: Mon, 20 Jul 2026 15:12:18 +0700 Subject: [PATCH 17/17] Validate rendered Indonesian localization --- scripts/validate-product-build.py | 147 ++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 30 deletions(-) diff --git a/scripts/validate-product-build.py b/scripts/validate-product-build.py index 823f44830..ce521ee1a 100644 --- a/scripts/validate-product-build.py +++ b/scripts/validate-product-build.py @@ -7,6 +7,7 @@ import re import struct import sys +import xml.etree.ElementTree as ET from html.parser import HTMLParser from pathlib import Path from urllib.parse import urlparse @@ -35,7 +36,21 @@ "goose-sequence.html", "cid-rejected.html", "live-model-vs-scl.html", "direct-vs-sbo.html", "commandtermination-addcause.html", } -LOCALIZED_PAGES = {"id.html", "panduan.html", "unduh.html"} +LOCALIZED_PAIRS = { + "index.html": "id.html", + "download.html": "unduh.html", + "guides.html": "panduan.html", + "mms-client.html": "mms-client-iec61850.html", + "smart-reporting.html": "smart-reporting-iec61850.html", + "goose-analyzer.html": "analyzer-goose-iec61850.html", + "file-transfer.html": "transfer-file-comtrade-iec61850.html", + "scl-workspace.html": "workspace-scl-iec61850.html", + "fat-testing.html": "pengujian-fat-iec61850.html", + "sat-testing.html": "pengujian-sat-iec61850.html", + "commissioning.html": "commissioning-iec61850.html", + "multi-vendor-integration.html": "integrasi-multi-vendor-iec61850.html", +} +LOCALIZED_PAGES = set(LOCALIZED_PAIRS.values()) FORBIDDEN_COPY = ( "without navigating source code", "without navigating the source repository", "without requiring repository navigation", "the website is the product front door", @@ -48,6 +63,7 @@ def __init__(self) -> None: self.refs: list[str] = [] self.icons: list[dict[str, str | None]] = [] self.images: list[dict[str, str | None]] = [] + self.alternates: dict[str, str] = {} self.h1 = 0 self.title = "" self.in_title = False @@ -74,8 +90,12 @@ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None value = values.get(key) if value: self.refs.append(value) - if tag == "link" and "icon" in (values.get("rel") or "").lower(): - self.icons.append(values) + if tag == "link": + rel = (values.get("rel") or "").lower() + if "icon" in rel: + self.icons.append(values) + if rel == "alternate" and values.get("hreflang") and values.get("href"): + self.alternates[values["hreflang"] or ""] = values["href"] or "" if tag == "img": self.images.append(values) @@ -103,6 +123,10 @@ def local_ref(site: Path, page: Path, reference: str) -> Path | None: return (page.parent / clean).resolve() +def page_url(name: str) -> str: + return CANONICAL_ROOT if name == "index.html" else CANONICAL_ROOT + name + + def validate_latest(site: Path, errors: list[str]) -> None: path = site / "latest.json" if not path.exists(): @@ -153,8 +177,8 @@ def validate_build_info(site: Path, errors: list[str]) -> tuple[str | None, list return version or None, [] if len(pages) != len(set(pages)): errors.append("build-info.json pages registry contains duplicates") - if len(pages) != 35: - errors.append(f"build-info.json must contain 35 pages, found {len(pages)}") + if len(pages) != 44: + errors.append(f"build-info.json must contain 44 pages, found {len(pages)}") if not GUIDE_PAGES.issubset(set(pages)): errors.append("build-info.json is missing troubleshooting guide pages") if not LOCALIZED_PAGES.issubset(set(pages)) or "technical-review.html" not in pages: @@ -167,27 +191,50 @@ def validate_sitemap(site: Path, pages: list[str], errors: list[str]) -> None: if not path.exists(): errors.append("missing sitemap.xml") return - text = path.read_text(encoding="utf-8") - if 'xmlns:xhtml="http://www.w3.org/1999/xhtml"' not in text: - errors.append("sitemap.xml is missing the xhtml localization namespace") - for page in pages: - if page == "404.html": - if CANONICAL_ROOT + page in text: - errors.append("sitemap.xml must not index 404.html") + try: + tree = ET.parse(path) + except ET.ParseError as exc: + errors.append(f"sitemap.xml parsing failed: {exc}") + return + ns = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9", "xhtml": "http://www.w3.org/1999/xhtml"} + root = tree.getroot() + if root.tag != "{http://www.sitemaps.org/schemas/sitemap/0.9}urlset": + errors.append("sitemap.xml has an invalid root namespace") + return + records: dict[str, dict[str, str]] = {} + for node in root.findall("sm:url", ns): + loc_node = node.find("sm:loc", ns) + if loc_node is None or not (loc_node.text or "").strip(): + errors.append("sitemap.xml contains a URL without loc") continue - url = CANONICAL_ROOT if page == "index.html" else CANONICAL_ROOT + page - if url not in text: - errors.append(f"sitemap.xml missing {page}") - alternate_sets = ( - (("en", CANONICAL_ROOT), ("id", CANONICAL_ROOT + "id.html"), ("x-default", CANONICAL_ROOT)), - (("en", CANONICAL_ROOT + "download.html"), ("id", CANONICAL_ROOT + "unduh.html"), ("x-default", CANONICAL_ROOT + "download.html")), - (("en", CANONICAL_ROOT + "guides.html"), ("id", CANONICAL_ROOT + "panduan.html"), ("x-default", CANONICAL_ROOT + "guides.html")), - ) - for alternate_set in alternate_sets: - for language, url in alternate_set: - marker = f'hreflang="{language}" href="{url}"' - if text.count(marker) < 2: - errors.append(f"sitemap.xml localization pair is incomplete: {marker}") + loc = (loc_node.text or "").strip() + if loc in records: + errors.append(f"sitemap.xml contains duplicate loc {loc}") + alternates: dict[str, str] = {} + for link in node.findall("xhtml:link", ns): + language, href = link.get("hreflang"), link.get("href") + if language and href: + alternates[language] = href + records[loc] = alternates + + expected_urls = {page_url(page) for page in pages if page != "404.html"} + if set(records) != expected_urls: + for missing in sorted(expected_urls - set(records)): + errors.append(f"sitemap.xml missing {missing}") + for extra in sorted(set(records) - expected_urls): + errors.append(f"sitemap.xml contains unexpected URL {extra}") + if len(records) != 43: + errors.append(f"sitemap.xml must contain 43 indexable URLs, found {len(records)}") + + for english, indonesian in LOCALIZED_PAIRS.items(): + expected = { + "en": page_url(english), + "id": page_url(indonesian), + "x-default": page_url(english), + } + for page in (english, indonesian): + if records.get(page_url(page)) != expected: + errors.append(f"sitemap.xml localized alternate set is incomplete for {page}") def validate_manifest(site: Path, icon_size: str, errors: list[str]) -> None: @@ -208,6 +255,17 @@ def validate_manifest(site: Path, icon_size: str, errors: list[str]) -> None: errors.append("site.webmanifest icon must support maskable purpose") +def expected_alternates_for(name: str) -> dict[str, str] | None: + for english, indonesian in LOCALIZED_PAIRS.items(): + if name in (english, indonesian): + return { + "en": page_url(english), + "id": page_url(indonesian), + "x-default": page_url(english), + } + return None + + def main() -> int: site = Path(sys.argv[1] if len(sys.argv) > 1 else "_site").resolve() errors: list[str] = [] @@ -232,6 +290,7 @@ def main() -> int: errors.append(f"app-icon.png: {exc}") combined = "" + parsers: dict[str, Parser] = {} for name in pages: page = site / name if not page.exists(): @@ -240,6 +299,7 @@ def main() -> int: combined += text parser = Parser() parser.feed(text) + parsers[name] = parser if parser.h1 != 1: errors.append(f"{name}: expected one h1") if not parser.title.strip() or not parser.description: @@ -274,6 +334,11 @@ def main() -> int: for value in (LINKEDIN, REPOSITORY, 'href="download.html"', 'href="technical-review.html"'): if value not in text: errors.append(f"{name}: shared authority or download route missing {value}") + expected_alternates = expected_alternates_for(name) + if expected_alternates is not None and parser.alternates != expected_alternates: + errors.append(f"{name}: page-level hreflang alternates are incomplete") + elif expected_alternates is None and parser.alternates: + errors.append(f"{name}: unexpected page-level language alternates") if name in GUIDE_PAGES: if parser.body_page != "guides": errors.append(f"{name}: troubleshooting guide must activate Guides navigation") @@ -287,6 +352,9 @@ def main() -> int: errors.append(f"{name}: Indonesian language metadata is incomplete") if 'hreflang="en"' not in text: errors.append(f"{name}: missing English counterpart link") + for value in ('href="unduh.html"', "Semua opsi unduhan"): + if value not in text: + errors.append(f"{name}: Indonesian download CTA is incomplete: {value}") for forbidden in ( "raw.githubusercontent.com/masarray/arsas/main/Assets/screenshot", @@ -300,6 +368,7 @@ def main() -> int: def page_text(name: str) -> str: path = site / name return path.read_text(encoding="utf-8") if path.exists() else "" + home, download, about = page_text("index.html"), page_text("download.html"), page_text("about.html") solutions, guides = page_text("solutions.html"), page_text("guides.html") review, id_home = page_text("technical-review.html"), page_text("id.html") @@ -322,12 +391,30 @@ def page_text(name: str) -> str: for value in ("Claim governance", "Not a conformance certificate", LINKEDIN, REPOSITORY): if value not in review: errors.append(f"technical review page missing {value}") - for value in ("Pengujian IEC 61850", 'href="panduan.html"', 'href="unduh.html"'): - if value not in id_home: - errors.append(f"Indonesian homepage missing {value}") - for value in (INSTALLER, PORTABLE, CHECKSUMS, "Unduh ARSAS"): + for localized in LOCALIZED_PAGES - {"id.html", "panduan.html", "unduh.html"}: + if f'href="{localized}"' not in id_home and f'href="{localized}"' not in id_guides: + errors.append(f"Indonesian hubs do not link to {localized}") + for value in (INSTALLER, PORTABLE, CHECKSUMS, "Unduh ARSAS", "Semua opsi unduhan"): if value not in id_download: errors.append(f"Indonesian download page missing {value}") + + localized_contracts = { + "mms-client-iec61850.html": ("MMS Client", "DataSet"), + "smart-reporting-iec61850.html": ("BRCB", "URCB"), + "analyzer-goose-iec61850.html": ("stNum", "sqNum"), + "transfer-file-comtrade-iec61850.html": ("COMTRADE", "CFG"), + "workspace-scl-iec61850.html": ("Edition 1", "selected-RCB"), + "pengujian-fat-iec61850.html": ("Pengujian FAT", "GOOSE"), + "pengujian-sat-iec61850.html": ("Pengujian SAT", "station"), + "commissioning-iec61850.html": ("Commissioning", "SOE"), + "integrasi-multi-vendor-iec61850.html": ("multi-vendor", "selected-RCB"), + } + for name, values in localized_contracts.items(): + text = page_text(name) + for value in values: + if value not in text: + errors.append(f"{name}: missing translated technical content {value}") + if version and f'"softwareVersion":"{version}"' not in home.replace(" ", ""): errors.append("homepage softwareVersion does not match build-info.json") @@ -349,7 +436,7 @@ def page_text(name: str) -> str: for error in errors: print(f"- {error}", file=sys.stderr) return 1 - print(f"ARSAS product-build validation passed: {len(pages)} pages, 11 guides, 3 Indonesian pages, authority policy, IndexNow and {icon_size} brand artwork.") + print(f"ARSAS product-build validation passed: {len(pages)} pages, 11 guides, 12 Indonesian pages, 12 hreflang pairs, authority policy, IndexNow and {icon_size} brand artwork.") return 0