Mengatasi Permasalahan Retrieve Failed Saat Ke Server Orthanc

Saya jelaskan ya mas, berdasarkan tampilan di layar:

Hasil Tes

  • Connection Test: Passed ✅

  • QUERY: Passed ✅

  • STORE: Passed ✅

  • RETRIEVE:

    • Pertama kali: Passed ✅

    • Di Configuration Test: Failed ❌ → Zero entities fetched

Analisis

  1. Tes koneksi berhasil → artinya komunikasi ke server Orthanc (192.168.20.221) tidak ada masalah.

  2. STORE berhasil → artinya server Orthanc bisa menerima data dari modalitas/mesin ini.

  3. QUERY juga berhasil → artinya server bisa di-ping untuk permintaan daftar data.

  4. RETRIEVE gagal (Zero entities fetched) → ini biasanya bukan karena jaringan, tapi karena tidak ada data (study/image) yang bisa diambil dari server Orthanc untuk AETitle yang dites.

Kemungkinan Penyebab

  • Orthanc belum punya study yang bisa diretrieve (kosong).

  • Query dilakukan dengan filter tertentu (misalnya PatientID) yang tidak ada di Orthanc.

  • Konfigurasi Retrieve AE Title di Orthanc belum diset dengan benar (misalnya tidak mengenali AE Title dari modalitas ini sebagai authorized peer).

  • Ada mismatch antara AE Title / Port / Hostname di setting mesin dengan yang didaftarkan di Orthanc.

Solusi yang bisa dicoba

  1. Cek isi Orthanc

    • Buka web interface Orthanc (http://192.168.20.221:8042 default) → login → pastikan ada study/image.

  2. Tes kirim data

    • Coba kirim 1 study dari mesin ke Orthanc, lalu lakukan retrieve lagi.

  3. Cek config Orthanc

    • Pastikan di file orthanc.json sudah ada konfigurasi AE Title dari modalitas ini di DicomModalities.

    • Contoh:

      "DicomModalities" : {
      "CT1" : [ "CT_AE", "192.168.20.100", 104 ]
      }
  4. Tes retrieve manual

    • Bisa pakai storescu atau getscu (jika ada toolkit DCMTK) untuk memastikan retrieve berjalan dari sisi lain.

👉 Intinya: masalahnya bukan di koneksi, tapi Orthanc tidak ada data atau tidak bisa memberikan data balik ke modalitas karena konfigurasi AE Title/Retrieve-nya.

Ini contoh orthanc.json minimal yang siap untuk QUERY/STORE/RETRIEVE dari modalitas. Silakan sesuaikan IP, AE Title, dan port-nya.

{
"Name": "ORTHANC",
"DicomAet": "ORTHANC", // AE Title Orthanc (yang Anda tes)
"DicomPort": 4242, // Port DICOM Orthanc (default 4242)
"HttpServerEnabled": true,
"HttpPort": 8042, // Web UI Orthanc
"AuthenticationEnabled": false, // set true jika pakai auth

// DAFTAR PEER untuk C-MOVE (HARUS ada AE si modalitas/viewer tujuan retrieve)
// Format: "Alias": ["AETitleTujuan", "IP_Tujuan", PortTujuan]
"DicomModalities": {
"XRAYROOM": ["XRAY_AE", "192.168.20.30", 104],
"USROOM": ["US_AE", "192.168.20.40", 104],
"VIEWERPC": ["VIEWERAE", "192.168.20.50", 11112],

// Tambahkan modalitas yang sedang Anda pakai untuk test retrieve:
// Ganti dengan AE Title & IP PC/mesin Anda
"MY_MODALITY": ["MYMODAE", "192.168.20.100", 104]
},

// Opsi umum (boleh dibiarkan default)
"StoreAnonymous": false,
"OverwriteInstances": false,
"SslEnabled": false
}

Cara pakai & checklist

  1. Pastikan identitas Orthanc

    • AE Title Orthanc = ORTHANC

    • IP Orthanc = 192.168.20.221

    • Port DICOM Orthanc = 4242

  2. Daftarkan modalitas tujuan C-MOVE
    Di blok DicomModalities wajib ada entri untuk PC/mesin yang melakukan retrieve.
    Contoh jika mesin Anda: AE = MYMODAE, IP = 192.168.20.100, port = 104, maka baris:

    "MY_MODALITY": ["MYMODAE", "192.168.20.100", 104]
  3. Restart Orthanc setelah ubah file config.

  4. Buka firewall di PC/mesin tujuan pada port DICOM-nya (104/11112 dlsb) agar Orthanc bisa mendorong image saat C-MOVE.

  5. Uji alur:

    • Kirim 1 study ke Orthanc (STORE).

    • Dari modalitas, lakukan C-FIND (Query) → harus muncul study.

    • Lakukan C-MOVE (Retrieve) dengan Destination AE = AE modalitas Anda (MYMODAE). Orthanc akan push ke IP/port yang didefinisikan pada DicomModalities.

Catatan:

  • Banyak mesin hanya melakukan C-MOVE (bukan C-GET). Untuk C-MOVE, Orthanc harus tahu AE/IP/port tujuan di DicomModalities. Kalau tidak, muncul kasus “Zero entities fetched”/gagal retrieve walau query lulus.

  • Jika tetap kosong: pastikan filter Query (PatientID/StudyDate) tidak menyaring semua data dan memang ada study di Orthanc.

Ditulis pada Tak Berkategori | Tinggalkan komentar

Source Code SSL Untuk TTE

import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;

private void BtnSimpanTandaTanganActionPerformed(java.awt.event.ActionEvent evt) {
R4.setSelected(true);
if (Phrase.getText().equals(“”)) {
Valid.textKosong(Phrase, “Phrase”);
} else {
try {
esign = true;
sertisign = false;
tampilPerawatan();
if (esign == true) {
File g = new File(“file.css”);
BufferedWriter bg = new BufferedWriter(new FileWriter(g));
bg.write(“.isi td{border-right: 1px solid #e2e7dd;font: 8.5px tahoma;height:12px;border-bottom: 1px solid #e2e7dd;background: #ffffff;color:#323232;}.isi a{text-decoration:none;color:#8b9b95;padding:0 0 0 0px;font-family: Tahoma;font-size: 8.5px;border: white;}”);
bg.close();

PdfWriter pdf = new PdfWriter(“RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf”);
HtmlConverter.convertToPdf(
LoadHTMLRiwayatPerawatan.getText().replaceAll(“<head>”, “<head><link href=\”file.css\” rel=\”stylesheet\” type=\”text/css\” />”).
replaceAll(“<body>”,
“<body>”
+ “<table width=’100%’ align=’center’ border=’0′ class=’tbl_form’ cellspacing=’0′ cellpadding=’0′>”
+ “<tr>”
+ “<td width=’15%’ border=’0′>”
+ “<img width=’50’ height=’50’ src=’data:image/jpeg;base64,” + Base64.getEncoder().encodeToString(Sequel.cariGambar(“select setting.logo from setting”).readAllBytes()) + “‘/>”
+ “</td>”
+ “<td width=’85%’ border=’0′>”
+ “<center>”
+ “<font color=’000000′ size=’3′ face=’Tahoma’>” + akses.getnamars() + “</font><br>”
+ “<font color=’000000′ size=’1′ face=’Tahoma’>”
+ akses.getalamatrs() + “, ” + akses.getkabupatenrs() + “, ” + akses.getpropinsirs() + “<br/>”
+ akses.getkontakrs() + “, E-mail : ” + akses.getemailrs()
+ “<br>RIWAYAT PERAWATAN”
+ “</font> ”
+ “</center>”
+ “</td>”
+ “</tr>”
+ “</table><br>”
+ “<table width=’100%’ border=’0′ align=’center’ cellpadding=’3px’ cellspacing=’0′ class=’tbl_form’>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>No.RM</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + NoRM.getText().trim() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Nama Pasien</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + NmPasien.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Alamat</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Alamat.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Jenis Kelamin</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Jk.getText().replaceAll(“L”, “Laki-Laki”).replaceAll(“P”, “Perempuan”) + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Tempat & Tanggal Lahir</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + TempatLahir.getText() + ” ” + TanggalLahir.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Ibu Kandung</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + IbuKandung.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Golongan Darah</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + GD.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Status Nikah</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + StatusNikah.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Agama</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Agama.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Pendidikan Terakhir</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Pendidikan.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Bahasa Dipakai</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + Bahasa.getText() + “</td>”
+ “</tr>”
+ “<tr class=’isi’>”
+ “<td valign=’top’ width=’20%’>Cacat Fisik</td>”
+ “<td valign=’top’ width=’1%’ align=’center’>:</td>”
+ “<td valign=’top’ width=’79%’>” + CacatFisik.getText() + “</td>”
+ “</tr>”
+ “</table>”
).
replaceAll((getClass().getResource(“/picture/”)) + “”, “./gambar/”), pdf
);
System.out.println(“Membuat ulang File RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf untuk dikirim ke server”);
File f = new File(“RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf”);
try {
CloseableHttpClient httpClient = createHttpClientWithSSL();
HttpPost post = new HttpPost(koneksiDB.URLAKSESFILEESIGN());
post.setHeader(“Content-Type”, “application/json”);
post.addHeader(“username”, koneksiDB.USERNAMEAPIESIGN());
post.addHeader(“password”, koneksiDB.PASSAPIESIGN());
post.addHeader(“url”, koneksiDB.URLAPIESIGN());

byte[] fileContent = Files.readAllBytes(f.toPath());

json = “{”
+ “\”file\”:\”” + Base64.getEncoder().encodeToString(fileContent) + “\”,”
+ “\”nik\”:\”” + Sequel.cariIsi(“select pegawai.no_ktp from pegawai where pegawai.nik=?”, akses.getkode()) + “\”,”
+ “\”passphrase\”:\”” + Phrase.getText() + “\”,”
+ “\”tampilan\”:\”visible\”,”
+ “\”image\”:\”false\”,”
+ “\”linkQR\”:\”Dikeluarkan di ” + akses.getnamars() + “, Kabupaten/Kota ” + akses.getkabupatenrs() + “. Ditandatangani secara elektronik oleh ” + dpjp + ” ID ” + kddpjp + ” Tanggal ” + Tanggal.getSelectedItem().toString() + “\”,”
+ “\”width\”:\”70\”,”
+ “\”height\”:\”70\”,”
+ “\”tag_koordinat\”:\”#\””
+ “}”;

System.out.println(“URL Akses file :” + koneksiDB.URLAKSESFILEESIGN());
post.setEntity(new StringEntity(json));
try ( CloseableHttpResponse response = httpClient.execute(post)) {
System.out.println(“Response Status : ” + response.getCode());
json = EntityUtils.toString(response.getEntity());
root = mapper.readTree(json);
if (response.getCode() == 200) {
try ( FileOutputStream fos = new FileOutputStream(new File(“RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf”))) {
byte[] fileBytes = Base64.getDecoder().decode(root.path(“response”).asText());
fos.write(fileBytes);
WindowPhrase.dispose();
JOptionPane.showMessageDialog(null, “Proses tanda tangan berhasil…”);
Desktop.getDesktop().browse(new File(“RPP” + NoRawat.getText().trim().replaceAll(“/”, “”) + “.pdf”).toURI());
} catch (Exception e) {
WindowPhrase.dispose();
JOptionPane.showMessageDialog(null, “Gagal mengkonversi base64 ke file…”);
System.out.println(“Notif : ” + e);
}
} else {
WindowPhrase.dispose();
JOptionPane.showMessageDialog(null, “Code : ” + root.path(“metadata”).path(“code”).asText() + ” Pesan : ” + root.path(“metadata”).path(“message”).asText());
}
} catch (IOException a) {
WindowPhrase.dispose();
System.out.println(“Notifikasi : ” + a);
JOptionPane.showMessageDialog(null, “” + a);
}
} catch (Exception e) {
WindowPhrase.dispose();
System.out.println(“Notifikasi : ” + e);
JOptionPane.showMessageDialog(null, “” + e);
}
}
} catch (Exception e) {
System.out.println(“Notifikasi : ” + e);
}
}
}

// Method untuk membuat HTTP client yang menerima semua SSL certificate
private CloseableHttpClient createHttpClientWithSSL() throws Exception {
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new TrustAllStrategy())
.build();

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext,
NoopHostnameVerifier.INSTANCE
);

HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslSocketFactory)
.build();

return HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
}

Ditulis pada Tak Berkategori | Tinggalkan komentar

Contoh Script Text to Speech Di Antrian Farmasi Pake Webspeech API

<!DOCTYPE html>
<html lang=”id”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Panggilan Penyerahan Resep</title>
<style>
.card {
background-color: #ff5722;
color: white;
padding: 20px;
border-radius: 8px;
margin: 10px 0;
}
.debug-info {
margin-top: 20px;
padding: 10px;
background-color: #f5f5f5;
border-radius: 4px;
font-size: 12px;
}
</style>
</head>
<body>
<div class=”row”>
<div class=”col s12″ id=”header-instansi”>
<div class=”card”>
<div class=”card-content”>
<h5>
<table border=’0′ width=’100%’>
<tr>
<td>Panggilan Penyerahan Resep</td>
<td>:</td>
<td>
<!– Simulasi data dari PHP –>
<span>12345 REG001 Nyonya Siti Aminah</span>
<span id=’namaPasien’ data-nama=’Nyonya Siti Aminah’></span>
</td>
</tr>
</table>
</h5>
</div>
</div>
</div>
</div>

<div class=”debug-info”>
<strong>Debug Info:</strong>
<div id=”debugInfo”></div>
<button onclick=”testSpeech()”>Test Suara Manual</button>
<button onclick=”showAvailableVoices()”>Tampilkan Voice Tersedia</button>
</div>

<script>
let speechReady = false;
let availableVoices = [];
let debugElement = document.getElementById(‘debugInfo’);

function log(message) {
console.log(message);
if (debugElement) {
debugElement.innerHTML += ‘<br>’ + message;
}
}

function initializeSpeech() {
// Cek dukungan browser
if (!(‘speechSynthesis’ in window)) {
log(‘❌ Browser tidak mendukung Web Speech API’);
return false;
}

log(‘✅ Web Speech API didukung’);
return true;
}

function loadVoices() {
availableVoices = window.speechSynthesis.getVoices();
log(`📢 ${availableVoices.length} suara tersedia`);

// Cari suara bahasa Indonesia
const indonesiaVoices = availableVoices.filter(voice =>
voice.lang.includes(‘id’) || voice.lang.includes(‘ID’)
);

if (indonesiaVoices.length > 0) {
log(`🇮🇩 Ditemukan ${indonesiaVoices.length} suara bahasa Indonesia`);
speechReady = true;

// Panggil pasien jika ada data
const namaPasienElem = document.getElementById(‘namaPasien’);
if (namaPasienElem && namaPasienElem.dataset.nama) {
setTimeout(() => {
panggilPasien(namaPasienElem.dataset.nama);
}, 500); // Delay sedikit untuk memastikan voices siap
}
} else {
log(‘⚠️ Suara bahasa Indonesia tidak ditemukan, akan menggunakan suara default’);
speechReady = true;
}
}

function panggilPasien(namaPasien) {
if (!speechReady) {
log(‘❌ Speech belum siap’);
return;
}

// Hentikan speech yang sedang berjalan
window.speechSynthesis.cancel();

const suara = new SpeechSynthesisUtterance();
suara.text = `Pasien dengan nama ${namaPasien}, silakan menuju loket penyerahan obat.`;

// Pengaturan suara
suara.lang = ‘id-ID’;
suara.rate = 0.8; // Sedikit lebih lambat agar jelas
suara.pitch = 1.0;
suara.volume = 1.0;

// Pilih suara terbaik
const indonesiaVoice = availableVoices.find(voice =>
voice.lang === ‘id-ID’ || voice.lang.includes(‘id’)
);

if (indonesiaVoice) {
suara.voice = indonesiaVoice;
log(`🎤 Menggunakan suara: ${indonesiaVoice.name} (${indonesiaVoice.lang})`);
} else {
log(‘🎤 Menggunakan suara default’);
}

// Event handlers
suara.onstart = function() {
log(‘▶️ Mulai berbicara’);
};

suara.onend = function() {
log(‘✅ Selesai berbicara’);
};

suara.onerror = function(event) {
log(`❌ Error: ${event.error}`);
};

// Jalankan Text-to-Speech
try {
window.speechSynthesis.speak(suara);
log(`📢 Memanggil: ${namaPasien}`);
} catch (error) {
log(`❌ Gagal memanggil: ${error.message}`);
}
}

function testSpeech() {
panggilPasien(‘Test Pasien’);
}

function showAvailableVoices() {
log(‘— DAFTAR SUARA TERSEDIA —‘);
availableVoices.forEach((voice, index) => {
log(`${index + 1}. ${voice.name} (${voice.lang}) – ${voice.localService ? ‘Lokal’ : ‘Online’}`);
});
}

// Inisialisasi
if (initializeSpeech()) {
// Load voices saat halaman dimuat
if (window.speechSynthesis.getVoices().length > 0) {
loadVoices();
}

// Event listener untuk voices changed
window.speechSynthesis.onvoiceschanged = function() {
log(‘🔄 Voices berubah, memuat ulang…’);
loadVoices();
};

// Fallback: coba load voices setelah delay
setTimeout(() => {
if (!speechReady) {
log(‘⏰ Fallback: memuat voices setelah delay’);
loadVoices();
}
}, 1000);
}

// Cleanup saat halaman akan ditutup
window.addEventListener(‘beforeunload’, function() {
window.speechSynthesis.cancel();
});
</script>
</body>
</html>

Ditulis pada Tak Berkategori | Tinggalkan komentar

Memberikan Background Merah di Link Berkas Digital

private void menampilkanBerkasDigital(String norawat) {
try {
if(chkBerkasDigital.isSelected()==true){
try{
rs2=koneksi.prepareStatement(
“select master_berkas_digital.nama,berkas_digital_perawatan.lokasi_file “+
“from berkas_digital_perawatan inner join master_berkas_digital “+
“on berkas_digital_perawatan.kode=master_berkas_digital.kode “+
“where berkas_digital_perawatan.no_rawat='”+norawat+”‘”).executeQuery();
if(rs2.next()){
htmlContent.append(
“<tr class=’isi’>”).append(
“<td valign=’top’ width=’2%’></td>”).append(
“<td valign=’top’ width=’18%’>Berkas Digital Perawatan</td>”).append(
“<td valign=’top’ width=’1%’ align=’center’>:</td>”).append(
“<td valign=’top’ width=’79%’>”).append(
“<table width=’100%’ border=’0′ align=’center’ cellpadding=’3px’ cellspacing=’0′ class=’tbl_form’>”).append(
“<tr align=’center’>”).append(
“<td valign=’top’ width=’4%’ bgcolor=’#FFFAF8′>No.</td>”).append(
“<td valign=’top’ width=’96%’ bgcolor=’#FFFAF8′>Berkas Digital</td>”).append(
“</tr>”
);
w=1;
do{
if(rs2.getString(“lokasi_file”).toLowerCase().contains(“.jpg”)||rs2.getString(“lokasi_file”).toLowerCase().contains(“.jpeg”)){
htmlContent.append(
“<tr>”).append(
“<td valign=’top’ align=’center’>”).append(w).append(“</td>”).append(
“<td valign=’top’ align=’center’>”).append(rs2.getString(“nama”)).append(“<br><a href=’http://”).append(koneksiDB.HOSTHYBRIDWEB()).append(“:”).append(koneksiDB.PORTWEB()).append(“/”).append(koneksiDB.HYBRIDWEB()).append(“/berkasrawat/”).append(rs2.getString(“lokasi_file”)).append(“‘><img alt=’Berkas Digital’ src=’http://”).append(koneksiDB.HOSTHYBRIDWEB()).append(“:”).append(koneksiDB.PORTWEB()).append(“/”).append(koneksiDB.HYBRIDWEB()).append(“/berkasrawat/”).append(rs2.getString(“lokasi_file”)).append(“‘ width=’450′ height=’450’/></a></td>”).append(
“</tr>”);
}else{
htmlContent.append(
“<tr>”).append(
“<td valign=’top’ align=’center’>”).append(w).append(“</td>”).append(
“<td valign=’top’><a href=’http://”).append(koneksiDB.HOSTHYBRIDWEB()).append(“:”).append(koneksiDB.PORTWEB()).append(“/”).append(koneksiDB.HYBRIDWEB()).append(“/berkasrawat/”).append(rs2.getString(“lokasi_file”)).append(“‘ style=’background-color: #8B0000; color: white; font-weight: bold; text-decoration: none; display: inline-block; padding: 8px 12px; border-radius: 4px; transition: background-color 0.3s;’ onmouseover=’this.style.backgroundColor=\”#A00000\”‘ onmouseout=’this.style.backgroundColor=\”#8B0000\”‘>”).append(rs2.getString(“nama”)).append(“_”).append(rs2.getString(“lokasi_file”).replaceAll(“pages/upload/”,””)).append(“</a></td>”).append(
“</tr>”);
}
w++;
}while(rs2.next());
htmlContent.append(
“</table>”).append(
“</td>”).append(
“</tr>”);
}
} catch (Exception e) {
System.out.println(“Notifikasi : “+e);
} finally{
if(rs2!=null){
rs2.close();
}
}
}
}catch (Exception e) {
System.out.println(“Notif Berkas Digitaln: “+e);
}
}

Ditulis pada Tak Berkategori | Tinggalkan komentar

Membuat Format Nomor Mengandung Bulan dan Tanggal

// Tambahkan method baru untuk generate nomor rujukan dengan format yang diinginkan
private void generateNoRujukan() {
try {
// Ambil tanggal saat ini
Calendar cal = Calendar.getInstance();
int tahun = cal.get(Calendar.YEAR);
int bulan = cal.get(Calendar.MONTH) + 1; // Calendar.MONTH dimulai dari 0

// Konversi bulan ke angka romawi
String bulanRomawi = convertToRoman(bulan);

// Query untuk mencari nomor urut terakhir dalam bulan dan tahun yang sama
String sql = “SELECT MAX(CAST(SUBSTRING_INDEX(no_rujuk, ‘/’, 1) AS UNSIGNED)) as max_no ” +
“FROM rujuk ” +
“WHERE YEAR(tgl_rujuk) = ? AND MONTH(tgl_rujuk) = ?”;

PreparedStatement ps = koneksi.prepareStatement(sql);
ps.setInt(1, tahun);
ps.setInt(2, bulan);

ResultSet rs = ps.executeQuery();

int nomorUrut = 1; // Default nomor urut
if (rs.next()) {
Integer maxNo = rs.getInt(“max_no”);
if (!rs.wasNull()) {
nomorUrut = maxNo + 1;
}
}

// Format nomor urut dengan leading zero (3 digit)
String nomorUrutFormatted = String.format(“%03d”, nomorUrut);

// Buat nomor rujukan dengan format: 001/SR/VIII/2025
String noRujukan = nomorUrutFormatted + “/SR/” + bulanRomawi + “/” + tahun;

TNoRj.setText(noRujukan);

rs.close();
ps.close();

} catch (Exception e) {
System.out.println(“Error generating nomor rujukan: ” + e);
// Fallback ke nomor default jika ada error
TNoRj.setText(“001/SR/I/” + Calendar.getInstance().get(Calendar.YEAR));
}
}

// Method untuk konversi bulan ke angka romawi
private String convertToRoman(int bulan) {
String[] bulanRomawi = {
“I”, “II”, “III”, “IV”, “V”, “VI”,
“VII”, “VIII”, “IX”, “X”, “XI”, “XII”
};

if (bulan >= 1 && bulan <= 12) {
return bulanRomawi[bulan – 1];
}
return “I”; // Default ke Januari jika ada error
}

// Modifikasi method emptTeks() untuk menggunakan format baru
public void emptTeks() {
TNoRj.setText(“”);
TTmpRujuk.setText(“”);
TNoRw.setText(“”);
TNoRM.setText(“”);
TPasien.setText(“”);
KdDok.setText(“”);
TDokter.setText(“”);
TDiagnosa.setText(“”);
DTPRujuk.setDate(new Date());

// Ganti baris ini:
// Valid.autoNomer(“rujuk”,”R”,9,TNoRj);
// Dengan:
generateNoRujukan();

TNoRj.requestFocus();
ktrujuk.setSelectedIndex(0);
ambulance.setSelectedIndex(0);
ket.setText(“”);
}

// Jika Anda ingin nomor rujukan juga ter-generate saat tanggal rujukan berubah,
// tambahkan modifikasi pada DTPRujukKeyPressed atau buat listener untuk DTPRujuk:
private void DTPRujukKeyPressed(java.awt.event.KeyEvent evt) {
Valid.pindah(evt,TNoRj,CmbJam);

// Regenerate nomor rujukan jika tanggal berubah
if (evt.getKeyCode() == KeyEvent.VK_ENTER || evt.getKeyCode() == KeyEvent.VK_TAB) {
generateNoRujukan();
}
}

// Atau bisa tambahkan ActionListener untuk DTPRujuk di constructor:
// Tambahkan ini di constructor DlgRujuk setelah inisialisasi komponen:
/*
DTPRujuk.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
generateNoRujukan();
}
});
*/

Ditulis pada Tak Berkategori | Tinggalkan komentar

Membuat HTTPS localhost (SSL) Menggunakan mkcert di Ubuntu 22.04

Berikut cara membuat HTTPS localhost menggunakan mkcert di Ubuntu 22.04:

1. Install mkcert

bash
# Install menggunakan curl
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert

# Atau install dari snap
sudo snap install mkcert

2. Setup Certificate Authority (CA)

bash
# Install CA root certificate ke system trust store
mkcert -install

Perintah ini akan membuat CA lokal dan menambahkannya ke trust store browser dan sistem.

3. Generate Certificate untuk Localhost

bash
# Generate certificate untuk localhost
mkcert localhost 127.0.0.1 ::1

# Atau untuk domain custom
mkcert example.local "*.example.local"

Ini akan menghasilkan dua file:

  • localhost+2.pem (certificate)
  • localhost+2-key.pem (private key)

4. Konfigurasi Web Server

Untuk Nginx:

nginx
server {
    listen 443 ssl;
    server_name localhost;
    
    ssl_certificate /path/to/localhost+2.pem;
    ssl_certificate_key /path/to/localhost+2-key.pem;
    
    location / {
        root /var/www/html;
        index index.html;
    }
}

Untuk Apache:

apache
<VirtualHost *:443>
    ServerName localhost
    DocumentRoot /var/www/html
    
    SSLEngine on
    SSLCertificateFile /path/to/localhost+2.pem
    SSLCertificateKeyFile /path/to/localhost+2-key.pem
</VirtualHost>

Untuk Node.js/Express:

javascript
const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

const options = {
    key: fs.readFileSync('localhost+2-key.pem'),
    cert: fs.readFileSync('localhost+2.pem')
};

https.createServer(options, app).listen(3000, () => {
    console.log('HTTPS Server running on https://localhost:3000');
});

5. Test Certificate

Buka browser dan akses https://localhost. Anda seharusnya melihat:

  • Tidak ada warning SSL
  • Icon gembok hijau di address bar
  • Certificate valid

Tips Tambahan:

Lokasi CA root certificate:

bash
# Cek lokasi CA
mkcert -CAROOT

Generate untuk multiple domains:

bash
mkcert localhost 127.0.0.1 ::1 example.local "*.example.local"

Uninstall CA (jika diperlukan):

bash
mkcert -uninstall

Dengan setup ini, Anda akan memiliki HTTPS localhost yang valid tanpa warning browser, sangat berguna untuk development aplikasi yang memerlukan HTTPS.

SM

untuk apache copykan kemana filenya dan virtual host dibuat dimana

Berikut detail lengkap untuk konfigurasi Apache di Ubuntu 22.04:

1. Lokasi File Certificate

Salin file certificate ke direktori yang aman:

bash
# Buat direktori untuk SSL certificates
sudo mkdir -p /etc/ssl/private
sudo mkdir -p /etc/ssl/certs

# Copy certificate files
sudo cp localhost+2.pem /etc/ssl/certs/
sudo cp localhost+2-key.pem /etc/ssl/private/

# Set permissions yang tepat
sudo chmod 644 /etc/ssl/certs/localhost+2.pem
sudo chmod 600 /etc/ssl/private/localhost+2-key.pem
sudo chown root:root /etc/ssl/certs/localhost+2.pem
sudo chown root:root /etc/ssl/private/localhost+2-key.pem

2. Buat Virtual Host

Lokasi Virtual Host di Ubuntu:

  • Available sites: /etc/apache2/sites-available/
  • Enabled sites: /etc/apache2/sites-enabled/

Buat file konfigurasi:

bash
# Buat file virtual host baru
sudo nano /etc/apache2/sites-available/localhost-ssl.conf

Isi file konfigurasi:

apache
<VirtualHost *:443>
    ServerName localhost
    DocumentRoot /var/www/html
    
    # SSL Configuration
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/localhost+2.pem
    SSLCertificateKeyFile /etc/ssl/private/localhost+2-key.pem
    
    # Optional: SSL Security Headers
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    Header always set X-Content-Type-Options nosniff
    Header always set X-Frame-Options DENY
    
    # Log files
    ErrorLog ${APACHE_LOG_DIR}/localhost_ssl_error.log
    CustomLog ${APACHE_LOG_DIR}/localhost_ssl_access.log combined
</VirtualHost>

# Optional: Redirect HTTP to HTTPS
<VirtualHost *:80>
    ServerName localhost
    Redirect permanent / https://localhost/
</VirtualHost>

3. Enable Modules dan Site

bash
# Enable SSL module
sudo a2enmod ssl
sudo a2enmod headers

# Enable site
sudo a2ensite localhost-ssl.conf

# Test konfigurasi
sudo apache2ctl configtest

# Restart Apache
sudo systemctl restart apache2

4. Struktur Direktori Lengkap

/etc/apache2/
├── sites-available/
│   ├── 000-default.conf
│   ├── default-ssl.conf
│   └── localhost-ssl.conf          # File yang kita buat
├── sites-enabled/
│   ├── 000-default.conf -> ../sites-available/000-default.conf
│   └── localhost-ssl.conf -> ../sites-available/localhost-ssl.conf
└── ...

/etc/ssl/
├── certs/
│   └── localhost+2.pem            # Certificate file
└── private/
    └── localhost+2-key.pem        # Private key

5. Verifikasi Setup

bash
# Cek apakah Apache listening di port 443
sudo netstat -tlnp | grep :443

# Cek enabled sites
sudo a2ensite --list

# Cek SSL certificate
openssl x509 -in /etc/ssl/certs/localhost+2.pem -text -noout

6. Test di Browser

  1. Buka https://localhost
  2. Pastikan tidak ada SSL warning
  3. Cek certificate details di browser

7. Troubleshooting

Jika ada error permission:

bash
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html

Jika Apache tidak start:

bash
# Cek syntax error
sudo apache2ctl configtest

# Cek Apache status
sudo systemctl status apache2

# Cek log error
sudo tail -f /var/log/apache2/error.log

Disable default SSL site jika konflik:

bash
sudo a2dissite default-ssl.conf
sudo systemctl reload apache2

Dengan konfigurasi ini, Anda akan memiliki HTTPS localhost yang berfungsi dengan baik di Apache pada Ubuntu 22.04.

Ditulis pada Tak Berkategori | Tinggalkan komentar

Cara Mengatur Agar Audio di Izinkan di Google Chrome

Berikut adalah cara mengatur agar audio diizinkan di Google Chrome:

Mengatur Izin Audio untuk Situs Tertentu:
1. Buka Google Chrome dan kunjungi situs yang ingin Anda izinkan audio.
2. Klik ikon kunci atau ikon info di sebelah kiri alamat situs di bilah alamat.
3. Klik pada “Site settings” atau “Pengaturan situs”.
4. Cari bagian “Permissions” atau “Izin” dan cari opsi “Sound” atau “Suara”.
5. Pilih opsi “Allow” atau “Izinkan” untuk mengizinkan audio pada situs tersebut.

Mengatur Izin Audio untuk Semua Situs:
1. Buka Google Chrome dan ketik chrome://settings/content/sound di bilah alamat.
2. Anda akan diarahkan ke halaman pengaturan suara.
3. Pilih opsi “Sites can ask to play sound” atau “Situs dapat meminta untuk memutar suara”.

Mengatur Izin Audio untuk Situs yang Diblokir:
1. Jika Anda telah memblokir audio untuk situs tertentu dan ingin mengizinkannya kembali, Anda dapat melakukan langkah-langkah berikut.
2. Buka Google Chrome dan ketik chrome://settings/content/sound di bilah alamat.
3. Cari bagian “Block” atau “Blokir” dan cari situs yang ingin Anda izinkan audio.
4. Klik pada ikon tiga titik di sebelah situs dan pilih “Remove” atau “Hapus”.

Dengan mengikuti langkah-langkah di atas, Anda dapat mengatur agar audio diizinkan di Google Chrome untuk situs tertentu atau semua situs.

Ditulis pada Tak Berkategori | Tinggalkan komentar

Cara Mengambil Tahun Pakai Query Mariadb dari Kolom Tanggal

Untuk mengambil tahun dari kolom tanggal di tabel pengajuan_cuti, Anda dapat menggunakan fungsi bawaan SQL seperti YEAR(). Berikut adalah query-nya:

sql
SELECT YEAR(tanggal) AS tahun
FROM pengajuan_cuti;

Penjelasan:

  • YEAR(tanggal): Mengambil tahun dari kolom tanggal, yang diasumsikan bertipe DATE, DATETIME, atau tipe data serupa.
  • AS tahun: Memberi alias untuk kolom hasil agar lebih mudah dibaca (opsional).

Jika Anda ingin hanya mengambil tahun tertentu atau data lainnya berdasarkan tahun, Anda bisa menambahkan klausa WHERE atau GROUP BY. Contoh:

Filter Berdasarkan Tahun Tertentu

sql
SELECT *
FROM pengajuan_cuti
WHERE YEAR(tanggal) = 2025;

Mengelompokkan Berdasarkan Tahun

sql
SELECT YEAR(tanggal) AS tahun, COUNT(*) AS jumlah_pengajuan
FROM pengajuan_cuti
GROUP BY YEAR(tanggal);

Silakan sesuaikan dengan kebutuhan Anda! 😊

Ditulis pada Tak Berkategori | Tinggalkan komentar

Mengupdate Salah Satu Kolom Tabel Database Berdasarkan Kolom di Tabel Database Lain

saya punya database mariadb dengan nama sik, didalamnya ada table reg_periksa dan surat_keterangan_sehat, didalam kedua table tersebut ada kolom no_rawat dan kd_dokter, bagaimana querynya mengupdate kd_dokter yang ditable surat_keterangan_sehat berdasarkan kd_dokter di reg_periksa sesuai no_rawat
ChatGPT said:
ChatGPT

Berikut adalah query SQL untuk memperbarui nilai kd_dokter di tabel surat_keterangan_sehat berdasarkan nilai kd_dokter di tabel reg_periksa dengan mencocokkan kolom no_rawat di kedua tabel:

sql
UPDATE surat_keterangan_sehat AS sks
JOIN reg_periksa AS rp
ON sks.no_rawat = rp.no_rawat
SET sks.kd_dokter = rp.kd_dokter;

Penjelasan:

  1. UPDATE surat_keterangan_sehat AS sks: Menentukan tabel yang akan diperbarui (surat_keterangan_sehat) dan memberikan alias sks untuk mempermudah penulisan.
  2. JOIN reg_periksa AS rp: Menggabungkan tabel reg_periksa dengan alias rp menggunakan JOIN.
  3. ON sks.no_rawat = rp.no_rawat: Menghubungkan kedua tabel berdasarkan kolom no_rawat.
  4. SET sks.kd_dokter = rp.kd_dokter: Mengatur kolom kd_dokter di tabel surat_keterangan_sehat agar bernilai sama dengan kolom kd_dokter dari tabel reg_periksa.

Catatan:

  • Pastikan kolom no_rawat di kedua tabel memiliki nilai yang cocok untuk penghubung.
  • Sebaiknya Anda membuat backup database terlebih dahulu sebelum menjalankan query ini, terutama jika dijalankan pada database produksi.
Ditulis pada Tak Berkategori | Tinggalkan komentar

Layout Manager Netbeans

Layout Manager di NetBeans adalah mekanisme yang digunakan untuk mengatur tata letak komponen dalam sebuah container (misalnya JPanel, JFrame, atau JDialog) di aplikasi Java GUI. NetBeans menyediakan GUI Builder (Matisse)yang memungkinkan pengembang mendesain antarmuka grafis secara visual tanpa menulis banyak kode secara manual.

Berikut adalah penjelasan tentang berbagai Layout Manager yang dapat digunakan di NetBeans:


1. Free Design (Default NetBeans Layout)

  • Free Design adalah tata letak default di GUI Builder NetBeans.
  • Memungkinkan komponen ditempatkan dan diatur dengan drag-and-drop secara bebas.
  • Komponen otomatis disejajarkan menggunakan panduan snap lines untuk memastikan tata letak yang rapi.
  • NetBeans secara otomatis menghasilkan kode berbasis GroupLayout di latar belakang.

Kelebihan:

  • Mudah digunakan untuk pemula.
  • Tidak memerlukan pemahaman mendalam tentang layout manager manual.
  • Desain bersifat visual dan intuitif.

Kekurangan:

  • Kode yang dihasilkan terkadang sulit dibaca atau diubah secara manual.
  • Tidak fleksibel untuk desain dinamis (misalnya saat layar diubah ukurannya).

2. BorderLayout

  • Membagi container menjadi lima area:
    • North (Atas)
    • South (Bawah)
    • East (Kanan)
    • West (Kiri)
    • Center (Tengah)
  • Setiap area hanya dapat menampung satu komponen.

Kelebihan:

  • Sederhana dan efektif untuk tata letak dengan hierarki jelas.
  • Cocok untuk aplikasi dengan header, footer, dan konten utama.

Kekurangan:

  • Kurang fleksibel jika lebih dari satu komponen dibutuhkan dalam satu area.

3. FlowLayout

  • Menempatkan komponen secara berurutan (dari kiri ke kanan) seperti teks dalam paragraf.
  • Komponen akan pindah ke baris berikutnya jika tidak ada ruang yang cukup.

Kelebihan:

  • Sangat sederhana untuk tata letak dasar.
  • Komponen otomatis mengatur posisi berdasarkan ukuran container.

Kekurangan:

  • Tidak ideal untuk tata letak yang kompleks.

4. GridLayout

  • Membagi container menjadi grid dengan jumlah baris dan kolom tetap.
  • Semua komponen memiliki ukuran yang sama.

Kelebihan:

  • Mudah digunakan untuk tata letak berbasis tabel.
  • Memastikan tata letak simetris.

Kekurangan:

  • Kurang fleksibel karena semua komponen memiliki ukuran yang sama.

5. GridBagLayout

  • Layout manager yang sangat fleksibel dan kuat.
  • Memungkinkan pengaturan posisi dan ukuran komponen berdasarkan grid yang lebih kompleks.
  • Setiap komponen dapat memiliki ukuran relatif yang berbeda.

Kelebihan:

  • Cocok untuk desain yang kompleks dan dinamis.
  • Mendukung perbedaan ukuran komponen.

Kekurangan:

  • Lebih sulit digunakan dan memerlukan pemahaman mendalam.

6. CardLayout

  • Menyusun komponen seperti tumpukan kartu, di mana hanya satu kartu yang terlihat pada satu waktu.
  • Cocok untuk membuat wizard atau tab view.

Kelebihan:

  • Sederhana untuk mengelola tata letak yang berubah-ubah.

Kekurangan:

  • Hanya satu komponen yang dapat terlihat dalam satu waktu.

7. Absolute Layout

  • Memungkinkan pengembang menentukan posisi dan ukuran komponen secara manual.
  • NetBeans GUI Builder juga mendukung ini, tetapi jarang digunakan karena kurang fleksibel.

Kelebihan:

  • Memberikan kontrol penuh atas tata letak.

Kekurangan:

  • Sulit diubah jika aplikasi dijalankan pada layar dengan ukuran berbeda.
  • Tidak mendukung responsivitas.

Tips Desain dengan Layout Manager di NetBeans:

  • Gunakan GUI Builder untuk mendesain secara visual: Drag-and-drop komponen, dan NetBeans akan memilih layout yang sesuai.
  • Tetapkan ukuran minimum, maksimum, dan preferensi komponen: Agar desain tetap rapi di berbagai resolusi.
  • Gunakan kombinasi layout: Anda bisa menanamkan panel dengan layout yang berbeda dalam container yang sama untuk hasil yang lebih kompleks.
  • Simulasikan di berbagai ukuran layar: Untuk memastikan desain tetap responsif.
Ditulis pada Tak Berkategori | Tinggalkan komentar