package id.co.gtc.erhacam; import BASS.AudioPlayer; import BASS.PlaybackStatus; import Camera.*; import Config.CameraConfigEnum; import Config.SomeCodes; import Database.PhotoReviewClass; import Database.Sqlite; import ErhaAPI.ErhaAPI; import ErhaAPI.BarcodeResullt; import ErhaAPI.PhotoResult; import ErhaAPI.PatientRecord; import ErhaAPI.UploadResult; import javafx.application.Platform; import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.control.TextArea; import javafx.scene.layout.AnchorPane; import javafx.stage.DirectoryChooser; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javafx.scene.control.Alert.AlertType; import org.bytedeco.javacv.OpenCVFrameGrabber; import org.bytedeco.javacv.VideoInputFrameGrabber; import org.bytedeco.opencv.opencv_core.Rect; import org.bytedeco.opencv.opencv_core.Size; import org.tinylog.Logger; import static Config.SomeCodes.*; import static org.bytedeco.opencv.global.opencv_videoio.CAP_DSHOW; public class CaptureView { @FXML private AnchorPane CaptureViewAnchor; @FXML private AnchorPane cam1; @FXML private AnchorPane cam2; @FXML private AnchorPane cam3; @FXML private AnchorPane cam4; @FXML private AnchorPane cam5; @FXML private AnchorPane controlpane; private Cameradetail image1, image2, image3, image4, image5; @FXML private TextArea barcodeData; @FXML private TextArea medicalRecordID; @FXML private TextArea PatientName; @FXML private Button TakePhotoButton; private AudioPlayer audioPlayer; private String audio_posisikan_muka = "posisikan_wajah.wav"; private String audio_scan_barcode = "scan_barcode.wav"; private String audio_pengambilan_gagal = "pengambilan_gagal.wav"; private String audio_pengambilan_berhasil = "pengambilan_berhasil.wav"; private String audio_upload_gagal = "upload_gagal.wav"; private String audio_countdown = "countdown321.wav"; private String audio_camera_shutter = "camera-shutter-click-01.wav"; private String audio_tahan_posisi = "tahan_posisi.wav"; private String audio_data_barcode_tidak_ditemukan = "data_barcode_tidak_ditemukan.wav"; private String audio_hubungi_staf_kami = "hubungistafkami.wav"; private List cams; private final AtomicBoolean[] have_face = new AtomicBoolean[5]; private final AtomicBoolean[] have_eyes = new AtomicBoolean[5]; private final AtomicBoolean[] have_profile = new AtomicBoolean[5]; private final AtomicBoolean isTakingPhoto = new AtomicBoolean(false); private final ErhaAPI erhaAPI = new ErhaAPI(false); // for timeout 180 detik private final int timeout = 180; ScheduledExecutorService timeoutExecutor = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture runningTask = null; private void TakePhotoButtonChange(){ boolean disabled = false; String barcode = barcodeData.getText(); String medicalrecord = medicalRecordID.getText(); String patientname = PatientName.getText(); if (barcode.isBlank()) disabled = true; if (medicalrecord.isBlank()) disabled = true; if (patientname.isBlank()) disabled = true; TakePhotoButton.setDisable(disabled); } @FXML private void ChangeDirectory(){ DirectoryChooser dc = new DirectoryChooser(); dc.setTitle("Select Directory"); String path = dc.showDialog(null).getAbsolutePath(); config.SetPhotoDirectory(path); config.Save(); } private void trigger_autofocus(Cameradetail image) { if (image!=null){ if (image.isCapturing()){ image.setAutoFocus(false); Wait(5); image.setFocus(0.7); Wait(5); image.setAutoFocus(true); Wait(5); } } } @FXML private void AutoFocus() { trigger_autofocus(image1); trigger_autofocus(image2); trigger_autofocus(image3); trigger_autofocus(image4); trigger_autofocus(image5); } @FXML private void TakePhotos(){ String directory = config.getPhotoDirectory(); String prefix = RemoveSpaces(medicalRecordID.getText()) ; boolean has_face = Arrays.stream(have_face).anyMatch(AtomicBoolean::get); if (ValidDirectory(directory)){ if (ValidMedicalRecordId(prefix)){ isTakingPhoto.set(true); if (has_face){ AutoCloseAlert.show("Pengambilan Foto", "Tahan Posisi Anda", "Proses ini kurang lebih 3 detik", 5, null); if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(), audio_tahan_posisi)){ audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_tahan_posisi, new PlaybackStatus() { @Override public void onPlaybackStarted(String filename) { } @Override public void onPlaybackFinished(String filename) { Wait(500); audioPlayer.PlayFile(audio_countdown, new PlaybackStatus() { @Override public void onPlaybackStarted(String filename) { } @Override public void onPlaybackFinished(String filename) { take_photo_lanjutan(directory, prefix); } @Override public void onPlaybackFailure(String filename) { } }); } @Override public void onPlaybackFailure(String filename) { } }); } } } else { AutoCloseAlert.show("Error", "No Face Detected", "No Face Detected", 5, null); isTakingPhoto.set(false); if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(),audio_posisikan_muka)) { audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_posisikan_muka, null); } } } } else { System.out.println("Prefix invalid, not taking photo"); isTakingPhoto.set(false); //AutoCloseAlert.show("QR Code Not Available", "", "Please scan QR before continue", 5, s -> AutoCloseAlert.show("Scan Barcode", "Silahkan Scan Barcode Anda", "Arahkan kertas barcode ke kamera", 0, null)); if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){ if (AutoCloseAlert.banner_01!=null){ System.out.println("Showing banner 01 because prefix invalid"); AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null); } } if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(), audio_scan_barcode)) { audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_scan_barcode, null); } } } } else { System.out.println("Photo Directory invalid, not taking photo"); isTakingPhoto.set(false); AutoCloseAlert.show("Invalid Photo Directory","Photo Directory not set", "Please set photo directory at Setting", 5, s -> AutoCloseAlert.show("Setting", "Setting", "Please set photo directory", 0, null)); } } static class CallablePhotoResult implements Callable { private final String directory; private final String prefix; private final Cameradetail image; public CallablePhotoResult(String directory, String prefix, Cameradetail image) { this.directory = directory; this.prefix = prefix; this.image = image; } @Override public PhotoResult call() { if (image!=null){ image.RemapROI(0.1, 0.3, false); double sharpness = CalculateSharpness(image.getGrayMat()); image.setSharpness_indicator(sharpness); PhotoResult p = image.TakePhoto(directory, prefix); p.setSharpscore(sharpness); if (ValidFile(p.getFullres())){ if (ValidFile(p.getCompressedfile())){ return p; } } } return null; } } AtomicInteger takephoto_failure_counter = new AtomicInteger(0); private void take_photo_lanjutan(String directory, String prefix){ audioPlayer.PlayFile(audio_camera_shutter, null); Size thumbsize = new Size(160,120); PhotoReviewClass prc = new PhotoReviewClass(); prc.setPrefix(prefix); long nanostart = System.nanoTime(); // for performance measurement ExecutorService executor = Executors.newFixedThreadPool(5); CallablePhotoResult task1 = new CallablePhotoResult(directory, prefix, image1); CallablePhotoResult task2 = new CallablePhotoResult(directory, prefix, image2); CallablePhotoResult task3 = new CallablePhotoResult(directory, prefix, image3); CallablePhotoResult task4 = new CallablePhotoResult(directory, prefix, image4); CallablePhotoResult task5 = new CallablePhotoResult(directory, prefix, image5); PhotoResult p1 = null; PhotoResult p2 = null; PhotoResult p3 = null; PhotoResult p4 = null; PhotoResult p5 = null; try{ Future f1 = executor.submit(task1); Future f2 = executor.submit(task2); Future f3 = executor.submit(task3); Future f4 = executor.submit(task4); Future f5 = executor.submit(task5); p1 = f1.get(); p2 = f2.get(); p3 = f3.get(); p4 = f4.get(); p5 = f5.get(); } catch (Exception e){ Logger.error("Error TakePhotos: " + e.getMessage()); } finally { executor.shutdown(); } String timestamp = SomeCodes.GetDateTimeString(); System.out.println("Creating timestamp: "+timestamp); // check for blurred image double score1=-1, score2=-1, score3=-1, score4=-1, score5=-1; if (p1!=null){ score1 = p1.getSharpscore(); } if (p2!=null){ score2 = p2.getSharpscore(); } if (p3!=null){ score3 = p3.getSharpscore(); } if (p4!=null){ score4 = p4.getSharpscore(); } if (p5!=null){ score5 = p5.getSharpscore(); } System.out.println("Sharpness score: "+score1+", "+score2+", "+score3+", "+score4+", "+score5); double lowest = FindLowestValue(score1,score2,score3,score4,score5); if (lowest<=config.getSharpnessThreshold()){ // jelek String culprit = ""; if (lowest==score1) culprit = "camera 1"; else if (lowest==score2) culprit = "camera 2"; else if (lowest==score3) culprit = "camera 3"; else if (lowest==score4) culprit = "camera 4"; else if (lowest==score5) culprit = "camera 5"; String lowest_string = String.format("%.2f", lowest); AutoCloseAlert.show("Take Photos Failed", "Blurred Image Detected", "Blurred Image Detected at "+culprit+" with sharpness score "+lowest_string, 5, s -> isTakingPhoto.set(false)); takephoto_failure_counter.incrementAndGet(); if (audioPlayer!=null && audioPlayer.isInited()){ if (takephoto_failure_counter.get() < 2){ if (!Objects.equals(audio_pengambilan_gagal, audioPlayer.getCurrentFile())){ audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_pengambilan_gagal, null); } } else { if (!Objects.equals(audio_hubungi_staf_kami, audioPlayer.getCurrentFile())){ audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_hubungi_staf_kami, null); } } } return; } else { // bagus takephoto_failure_counter.set(0); } Rect bestroi = null; Rect reducedroi = null; if (p1!=null && p1.getBestROI()!=null){ bestroi = p1.getBestROI(); reducedroi = p1.getReducedROI(); } if (p2!=null && p2.getBestROI()!=null){ if (bestroi==null) bestroi = p2.getBestROI(); else if (p2.getBestROI().area()>bestroi.area()) bestroi = p2.getBestROI(); if (reducedroi==null) reducedroi = p2.getReducedROI(); else if (p2.getReducedROI().area()>reducedroi.area()) reducedroi = p2.getReducedROI(); } if (p3!=null && p3.getBestROI()!=null){ if (bestroi==null) bestroi = p3.getBestROI(); else if (p3.getBestROI().area()>bestroi.area()) bestroi = p3.getBestROI(); if (reducedroi==null) reducedroi = p3.getReducedROI(); else if (p3.getReducedROI().area()>reducedroi.area()) reducedroi = p3.getReducedROI(); } if (p4!=null && p4.getBestROI()!=null){ if (bestroi==null) bestroi = p4.getBestROI(); else if (p4.getBestROI().area()>bestroi.area()) bestroi = p4.getBestROI(); if (reducedroi==null) reducedroi = p4.getReducedROI(); else if (p4.getReducedROI().area()>reducedroi.area()) reducedroi = p4.getReducedROI(); } if (p5!=null && p5.getBestROI()!=null){ if (bestroi==null) bestroi = p5.getBestROI(); else if (p5.getBestROI().area()>bestroi.area()) bestroi = p5.getBestROI(); if (reducedroi==null) reducedroi = p5.getReducedROI(); else if (p5.getReducedROI().area()>reducedroi.area()) reducedroi = p5.getReducedROI(); } if (p1!=null){ prc.setFileLeft90(ValidFile(p1.getFullres()) ? p1.getFullres() : ""); prc.setCompressedLeft90(ValidFile(p1.getCompressedfile()) ? p1.getCompressedfile() : ""); if (ValidFile(p1.getFullcrop())){ // ada crop prc.setCroppedLeft90(p1.getFullcrop()); } else { String xx = image1.CropBestMat(directory,prefix, bestroi); if (ValidFile(xx)) prc.setCroppedLeft90(xx); } if (ValidFile(p1.getCompressedcrop())){ prc.setCompressedCropLeft90(p1.getCompressedcrop()); } else { String xx =image1.CropReducedMat(directory,prefix, reducedroi); if (ValidFile(xx)) prc.setCompressedCropLeft90(xx); } String thumb1 = MakeThumbfile(p1.getFullres(), thumbsize); if (ValidFile(thumb1)) prc.setThumbLeft90(thumb1); } if (p2!=null){ prc.setFileLeft45(ValidFile(p2.getFullres()) ? p2.getFullres() : ""); prc.setCompressedLeft45(ValidFile(p2.getCompressedfile()) ? p2.getCompressedfile() : ""); if (ValidFile(p2.getFullcrop())){ // ada crop prc.setCroppedLeft45(p2.getFullcrop()); } else { String xx = image2.CropBestMat(directory,prefix, bestroi); if (ValidFile(xx)) prc.setCroppedLeft45(xx); } if (ValidFile(p2.getCompressedcrop())){ prc.setCompressedCropLeft45(p2.getCompressedcrop()); } else { String xx = image2.CropReducedMat(directory,prefix, reducedroi); if (ValidFile(xx)) prc.setCompressedCropLeft45(xx); } String thumb2 = MakeThumbfile(p2.getFullres(), thumbsize); if (ValidFile(thumb2)) prc.setThumbLeft45(thumb2); } if (p3!=null){ prc.setFileCenter(ValidFile(p3.getFullres()) ? p3.getFullres() : ""); prc.setCompressedCenter(ValidFile(p3.getCompressedfile()) ? p3.getCompressedfile() : ""); if (ValidFile(p3.getFullcrop())){ // ada crop prc.setCroppedCenter(p3.getFullcrop()); } else { String xx = image3.CropBestMat(directory,prefix, bestroi); if (ValidFile(xx)) prc.setCroppedCenter(xx); } if (ValidFile(p3.getCompressedcrop())){ prc.setCompressedCropCenter(p3.getCompressedcrop()); } else { String xx = image3.CropReducedMat(directory,prefix, reducedroi); if (ValidFile(xx)) prc.setCompressedCropCenter(xx); } String thumb3 = MakeThumbfile(p3.getFullres(), thumbsize); if (ValidFile(thumb3)) prc.setThumbCenter(thumb3); } if (p4!=null){ prc.setFileRight45(ValidFile(p4.getFullres()) ? p4.getFullres() : ""); prc.setCompressedRight45(ValidFile(p4.getCompressedfile()) ? p4.getCompressedfile() : ""); if (ValidFile(p4.getFullcrop())){ // ada crop prc.setCroppedRight45(p4.getFullcrop()); } else { String xx = image4.CropBestMat(directory,prefix, bestroi); if (ValidFile(xx)) prc.setCroppedRight45(xx); } if (ValidFile(p4.getCompressedcrop())){ prc.setCompressedCropRight45(p4.getCompressedcrop()); } else { String xx = image4.CropReducedMat(directory,prefix, reducedroi); if (ValidFile(xx)) prc.setCompressedCropRight45(xx); } String thumb4 = MakeThumbfile(p4.getFullres(), thumbsize); if (ValidFile(thumb4)) prc.setThumbRight45(thumb4); } if (p5!=null){ prc.setFileRight90(ValidFile(p5.getFullres()) ? p5.getFullres() : ""); prc.setCompressedRight90(ValidFile(p5.getCompressedfile()) ? p5.getCompressedfile() : ""); if (ValidFile(p5.getFullcrop())){ // ada crop prc.setCroppedRight90(p5.getFullcrop()); } else { String xx = image5.CropBestMat(directory,prefix, bestroi); if (ValidFile(xx)) prc.setCroppedRight90(xx); } if (ValidFile(p5.getCompressedcrop())){ prc.setCompressedCropRight90(p5.getCompressedcrop()); } else { String xx= image5.CropReducedMat(directory,prefix, reducedroi); if (ValidFile(xx)) prc.setCompressedCropRight90(xx); } String thumb5 = MakeThumbfile(p5.getFullres(), thumbsize); if (ValidFile(thumb5)) prc.setThumbRight90(thumb5); } long duration = (System.nanoTime() - nanostart) / 1000000; // in milliseconds System.out.println("TakePhotos duration: "+duration+" ms"); String[] files = prc.compressed(); if (files.length>0){ if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(),audio_pengambilan_berhasil)){ audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_pengambilan_berhasil, new PlaybackStatus() { @Override public void onPlaybackStarted(String filename) { } @Override public void onPlaybackFinished(String filename) { AutoCloseAlert.showpictures(prc.compressed(),3, (s)->{ if (AutoCloseAlert.banner_02!=null) { System.out.println("Showing banner 02 after photo taken"); AutoCloseAlert.showbanner(AutoCloseAlert.banner_02, 0, null); UploadFiles(prc, prefix); if (runningTask!=null) runningTask.cancel(false); } }); } @Override public void onPlaybackFailure(String filename) { } }); } } } else { if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(),audio_pengambilan_gagal)){ audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_pengambilan_gagal, null); } } clear(); if (runningTask!=null) runningTask.cancel(false); } } private void UploadFiles(PhotoReviewClass prc, String prefix){ String[] files = prc.compressed(); if (files.length>0){ InsertSQL(prc); Task uploadtask = new Task<>() { @Override protected Void call() { int totalfiles = files.length; int counter = 0; for (String ff : files) { UploadResult ur = erhaAPI.Upload_File(prefix, ff,true); if (ur != null) { if (ur.message.startsWith("Record has been created")) { counter++; updateMessage("Upload success for " + ff); } else updateMessage("Upload failed for " + ff+", Message : "+ur.message); } else updateMessage("Upload failed for " + ff+" because UploadResult is null"); } if (counter == totalfiles) { super.succeeded(); } else super.failed(); return null; } }; uploadtask.messageProperty().addListener((obs, oldval, newval)-> { System.out.println("UploadTask message: "+newval); Logger.info(newval); }); uploadtask.setOnSucceeded(e-> { System.out.println("UploadTask succeeded"); clear(); }); uploadtask.setOnFailed(e-> { System.out.println("UploadTask failed"); if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(), audio_upload_gagal)){ audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_upload_gagal, null); } } AutoCloseAlert.show("Upload Failed", "Upload Failed", "Upload Failed", 5, s -> clear()); }); Thread uploadThread = new Thread(uploadtask); uploadThread.setName("UploadThread MedicalRecordID="+prefix); uploadThread.setDaemon(true); uploadThread.start(); } else ShowAlert(AlertType.ERROR, "Error", "No Photos Taken", "No Photos Taken, please check camera"); } @FXML public void initialize(){ audio_posisikan_muka = ExtractResource("/posisikan_wajah.wav"); audio_pengambilan_berhasil = ExtractResource("/pengambilan_berhasil.wav"); audio_pengambilan_gagal = ExtractResource("/pengambilan_gagal.wav"); audio_scan_barcode = ExtractResource("/scan_barcode.wav"); audio_upload_gagal = ExtractResource("/upload_gagal.wav"); audio_countdown = ExtractResource("/countdown321.wav"); audio_camera_shutter = ExtractResource("/camera-shutter-click-01.wav"); audio_tahan_posisi = ExtractResource("/tahan_posisi.wav"); audio_data_barcode_tidak_ditemukan = ExtractResource("/data_barcode_tidak_ditemukan.wav"); audio_hubungi_staf_kami = ExtractResource("/hubungistafkami.wav"); //tambahan 19/03/2025 barcodeData.textProperty().addListener((observable, oldValue, newValue) -> { System.out.println("barcodeData changed from ["+oldValue+"] to ["+newValue+"]"); if (ValidBarCode(newValue)){ if (Objects.equals(AutoCloseAlert.banner_01, AutoCloseAlert.shownBanner)){ System.out.println("Close banner 01"); AutoCloseAlert.close(); } } else { if (AutoCloseAlert.banner_01!=null){ System.out.println("Show banner 01"); AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0, null); } } TakePhotoButtonChange(); }); medicalRecordID.textProperty().addListener((observable, oldValue, newValue) -> TakePhotoButtonChange()); PatientName.textProperty().addListener((observable, oldValue, newValue) -> TakePhotoButtonChange()); audioPlayer = new AudioPlayer(1,48000); Logger.info("Audio Player : "+(audioPlayer.isInited()? "Inited" : "Not Inited")); cams = null; try{ String[] xxx = VideoInputFrameGrabber.getDeviceDescriptions(); if (xxx!=null && xxx.length>0){ cams = Arrays.asList(xxx); } } catch (Exception e){ Logger.error("Error getting camera list: "+e.getMessage()); } LoadCameraDetail(cam1, 1, CameraConfigEnum.CameraConfigLeft90); LoadCameraDetail(cam2, 2, CameraConfigEnum.CameraConfigLeft45); LoadCameraDetail(cam3, 3, CameraConfigEnum.CameraConfigCenter); LoadCameraDetail(cam4, 4, CameraConfigEnum.CameraConfigRight45); LoadCameraDetail(cam5, 5, CameraConfigEnum.CameraConfigRight90); Platform.runLater(()->{ if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){ if (AutoCloseAlert.banner_01!=null){ System.out.println("Showing banner 01 for first time"); AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null); } } int indexleft90=-1; int indexleft45=-1; int indexcenter=-1; int indexright45=-1; int indexright90; if (cams!=null && !cams.isEmpty()){ String camleft90 = config.getCameraLeft90(); if (ValidString(camleft90)){ int[] indexes = FindIndexes(cams, camleft90); if (indexes.length>0){ indexleft90 = FindFirstIndex(cams, camleft90); Logger.info("Left90 Index: "+indexleft90); if (indexleft90!=-1){ final int finalindex = indexleft90; Thread c1 = new Thread(()-> SetupCameraWithController(image1, camleft90, finalindex)); c1.setName("SetupCameraWithController "+camleft90); c1.setDaemon(true); c1.start(); } } } String camleft45 = config.getCameraLeft45(); if (ValidString(camleft45)){ int[] indexes = FindIndexes(cams, camleft45); if (indexes.length>0){ indexleft45 = FindFirstIndex(cams, camleft45, indexleft90); Logger.info("Left45 Index: "+indexleft45); if (indexleft45!=-1) { final int finalindex = indexleft45; Thread c2 = new Thread(()-> SetupCameraWithController(image2, camleft45, finalindex)); c2.setName("SetupCameraWithController "+camleft45); c2.setDaemon(true); c2.start(); } } } String camcenter = config.getCameraCenter(); if (ValidString(camcenter)){ int[] indexes = FindIndexes(cams, camcenter); if (indexes.length>0){ indexcenter = FindFirstIndex(cams, camcenter, indexleft90, indexleft45); Logger.info("Center Index: "+indexcenter); if (indexcenter!=-1) { final int finalindex = indexcenter; Thread c3 = new Thread(()-> SetupCameraWithController(image3, camcenter, finalindex)); c3.setName("SetupCameraWithController "+camcenter); c3.setDaemon(true); c3.start(); } } } String camright45 = config.getCameraRight45(); if (ValidString(camright45)){ int[] indexes = FindIndexes(cams, camright45); if (indexes.length>0){ indexright45 = FindFirstIndex(cams, camright45, indexleft90, indexleft45, indexcenter); Logger.info("Right45 Index: "+indexright45); if (indexright45!=-1) { final int finalindex = indexright45; Thread c4 = new Thread(()-> SetupCameraWithController(image4, camright45, finalindex)); c4.setName("SetupCameraWithController "+camright45); c4.setDaemon(true); c4.start(); } } } String camright90 = config.getCameraRight90(); if (ValidString(camright90)){ int[] indexes = FindIndexes(cams, camright90); if (indexes.length>0){ indexright90 = FindFirstIndex(cams, camright90, indexleft90, indexleft45, indexcenter, indexright45); Logger.info("Right90 Index: "+indexright90); if (indexright90!=-1) { final int finalindex = indexright90; Thread c5 = new Thread(()-> SetupCameraWithController(image5, camright90, finalindex)); c5.setName("SetupCameraWithController "+camright90); c5.setDaemon(true); c5.start(); } } } } }); } private void clear(){ isTakingPhoto.set(false); TextAreaSetText(medicalRecordID,""); TextAreaSetText(PatientName,""); TextAreaSetText(barcodeData,""); } public void Unload(){ if (image1!=null) { image1.StopLiveView(); } if (image2!=null) { image2.StopLiveView(); } if (image3!=null) { image3.StopLiveView(); } if (image4!=null) { image4.StopLiveView(); } if (image5!=null) { image5.StopLiveView(); } config.Save(); } AtomicBoolean anti_bawel = new AtomicBoolean(false); private void SetupCameraWithController(Cameradetail image, String cameraname, int devicenumber){ if (image!=null){ String title = switch(image.getCameraConfigEnum()){ case CameraConfigCenter -> "03"; case CameraConfigLeft45 -> "02"; case CameraConfigLeft90 -> "01"; case CameraConfigRight45 -> "04"; case CameraConfigRight90 -> "05"; }; final AtomicBoolean _have_face = switch (image.getCameraConfigEnum()){ case CameraConfigCenter -> have_face[2]; case CameraConfigLeft45 -> have_face[1]; case CameraConfigLeft90 -> have_face[0]; case CameraConfigRight45 -> have_face[3]; case CameraConfigRight90 -> have_face[4]; }; final AtomicBoolean _have_profile = switch (image.getCameraConfigEnum()){ case CameraConfigCenter -> have_profile[2]; case CameraConfigLeft45 -> have_profile[1]; case CameraConfigLeft90 -> have_profile[0]; case CameraConfigRight45 -> have_profile[3]; case CameraConfigRight90 -> have_profile[4]; }; final AtomicBoolean _have_eye = switch (image.getCameraConfigEnum()){ case CameraConfigCenter -> have_eyes[2]; case CameraConfigLeft45 -> have_eyes[1]; case CameraConfigLeft90 -> have_eyes[0]; case CameraConfigRight45 -> have_eyes[3]; case CameraConfigRight90 -> have_eyes[4]; }; Platform.runLater(()-> image.setCameraTitle(title)); if (devicenumber!=-1){ // revisi 09/05/2025 dari new OpenCVFrameGrabber(devicenumber) OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(CAP_DSHOW+devicenumber); // default int livewidth = 640; int liveheight = 480; int photowidth = 640; int photoheight = 480; int reducewidth = 640; int reduceheight = 480; // mode1 selalu paling tinggi if (cameraname.contains("ACER QHD")){ photowidth = AcerQHD.ModeBest.getWidth(); photoheight = AcerQHD.ModeBest.getHeight(); livewidth = AcerQHD.ModeLive.getWidth(); liveheight = AcerQHD.ModeLive.getHeight(); } else if (cameraname.contains("AVerVision M15W")){ photowidth = AverVisionM15W.ModeBest.getWidth(); photoheight = AverVisionM15W.ModeBest.getHeight(); livewidth = AverVisionM15W.ModeLive.getWidth(); liveheight = AverVisionM15W.ModeLive.getHeight(); } else if (cameraname.contains("Arducam IMX477")){ // mode1 terbaik 16:9 // mode5 terbaik 4:3 photowidth = ArducamIMX477.ModeBest.getWidth(); photoheight = ArducamIMX477.ModeBest.getHeight(); livewidth = ArducamIMX477.ModeLive.getWidth(); liveheight = ArducamIMX477.ModeLive.getHeight(); } else if (cameraname.contains("OBSBOT Meet 2")){ photowidth = ObsbotMeet2.ModeBest.getWidth(); photoheight = ObsbotMeet2.ModeBest.getHeight(); livewidth = ObsbotMeet2.ModeLive.getWidth(); liveheight = ObsbotMeet2.ModeLive.getHeight(); reducewidth = ObsbotMeet2.Mode3.getWidth(); reduceheight = ObsbotMeet2.Mode3.getHeight(); } image.SetGrabber(devicenumber,grabber, livewidth,liveheight,photowidth,photoheight,reducewidth,reduceheight, true); boolean use_face_detector = true; boolean use_qr_detector = true; LiveCamEvent lce = new LiveCamEvent() { @Override public void onDetectedQRCode(String barCode) { barCode = RemoveSpaces(barCode); if (ValidBarCode(barCode)){ String prefix = barcodeData.getText(); if (!barCode.equals(prefix)){ final String finalbarCode = barCode; TextAreaSetText(barcodeData, finalbarCode); Task checkpatientID = new Task<>() { @Override protected PatientRecord call() throws Exception { BarcodeResullt br = erhaAPI.Validate_Barcode(finalbarCode,true); if (br!=null){ if (br.message.startsWith("Records found")){ if (br.data!=null && br.data.length>0){ PatientRecord pr = br.data[0]; if (!pr.medical_record_detail_id.isBlank()){ if (!pr.name.isBlank()){ super.succeeded(); return pr; } else { Logger.error("Barcode ",finalbarCode," PatientRecord name is empty"); throw new Exception("Barcode "+finalbarCode+" PatientRecord name kosong"); } } else { Logger.error("Barcode ",finalbarCode," PatientRecord medical_record_detail_id is empty"); throw new Exception("Data dengan barcode "+finalbarCode+", PatientRecord medical_record_detail_id kosong"); } } else { Logger.error("Record associated with barcode ",finalbarCode," is empty"); throw new Exception("Data dengan barcode "+finalbarCode+" ditemukan di server, tetapi kosong"); } } else { Logger.error("Record associated with barcode ",finalbarCode," is not found"); throw new Exception("Data dengan barcode "+finalbarCode+" tidak ditemukan di server"); } } else { Logger.error("BarcodeResullt with barcode ",finalbarCode," is null"); throw new Exception("BarcodeResult dengan barcode "+finalbarCode+" menghasilkan null"); } } }; checkpatientID.setOnSucceeded(event -> { PatientRecord pr = checkpatientID.getValue(); if (pr!=null){ int medrecid = toInt(pr.medical_record_detail_id); TextAreaSetText(medicalRecordID,""+medrecid); TextAreaSetText(PatientName, pr.name); runningTask = timeoutExecutor.schedule(()->{ // timeout System.out.println("runningTask timeout after "+timeout+" seconds"); clear(); }, timeout, TimeUnit.SECONDS); if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(),audio_posisikan_muka)) { if (!anti_bawel.get()){ audioPlayer.StopCurrentPlayback(); Wait(500); audioPlayer.PlayFile(audio_posisikan_muka, new PlaybackStatus() { @Override public void onPlaybackStarted(String filename) { anti_bawel.set(true); } @Override public void onPlaybackFinished(String filename) { anti_bawel.set(false); Thread antibawel = new Thread(()->{ try { Thread.sleep(15*1000); } catch (InterruptedException ignored) { } anti_bawel.set(false); }); antibawel.setName("anti bawel"); antibawel.setDaemon(true); antibawel.start(); } @Override public void onPlaybackFailure(String filename) { anti_bawel.set(false); } }); } } } } }); checkpatientID.setOnFailed(event -> { if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(), audio_data_barcode_tidak_ditemukan)) { audioPlayer.StopCurrentPlayback(); audioPlayer.PlayFile(audio_data_barcode_tidak_ditemukan,null); } } Task failed = (Task) event.getSource(); Throwable t = failed.getException(); final String message = t.getMessage(); System.out.println("checkpatientID.setOnFailed message : "+message); AutoCloseAlert.show("Data Tidak Ditemukan", "Pastikan data barcode anda benar", message , 5, s -> clear()); }); Thread checkpatientIDThread = new Thread(checkpatientID); checkpatientIDThread.setName("CheckPatientID barcode="+barCode); checkpatientIDThread.setDaemon(true); checkpatientIDThread.start(); } } } @Override public void onFrontalFaceDetector(boolean hasface, int width, int height) { if (hasface!= _have_face.get()){ _have_face.set(hasface); if (!hasface) { _have_eye.set(false); } //update_status(image); // instruksi scan barcode ketika welcomeUI masih muncul dan ada muka terdeteksi if (hasface && Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){ if (audioPlayer!=null && audioPlayer.isInited()){ if (!Objects.equals(audioPlayer.getCurrentFile(), audio_scan_barcode)) { audioPlayer.StopCurrentPlayback(); if (anti_bawel.get()) return; audioPlayer.PlayFile(audio_scan_barcode, new PlaybackStatus() { @Override public void onPlaybackStarted(String filename) { anti_bawel.set(true); } @Override public void onPlaybackFinished(String filename) { Thread antibawel = new Thread(()->{ try { Thread.sleep(15*1000); } catch (InterruptedException ignored) { } anti_bawel.set(false); }); antibawel.setName("anti bawel"); antibawel.setDaemon(true); antibawel.start(); } @Override public void onPlaybackFailure(String filename) { anti_bawel.set(false); } }); } } } } } @Override public void onProfileFaceDetector(boolean hasface, int width, int height) { if (hasface!= _have_profile.get()){ _have_profile.set(hasface); if (!hasface) { _have_eye.set(false); } //update_status(image); } } @Override public void onEyeDetector(boolean hasEye) { if (_have_face.get() || _have_profile.get()) _have_eye.set(hasEye); //update_status(image); } @Override public void onLog(String log) { String ss = String.format("[%s] : %s", title, log); Logger.info(ss); } @Override public void onBlink(int counter) { } @Override public void onDoubleBlink(int counter) { System.out.println("Double Blink detected at camera "+title+" delay= "+counter); if (audioPlayer!=null && audioPlayer.isPlaying()) return; // let audio finish playback if (isTakingPhoto.get()) return; // other camera is taking picture // revisi 08/04/2025 TakePhotos(); } @Override public void onStartCapturing() { } @Override public void onIntervalUpdate() { update_status(image); } private void update_status(Cameradetail image){ String sb = "Camera Started, " + image.getRealWidth() + "x" + image.getRealHeight() + "@" + image.getLiveFPS(); image.setCameraStatus(sb); } }; image.setCameraStatus("Camera Starting"); if (image.StartLiveView(lce, title, use_qr_detector, use_face_detector)){ //TODO Start Live View berhasil, apa lagi yang mau dikerjakan ? } else image.setCameraStatus("Unable to Set Grabber"); } else image.setCameraStatus("Camera not found, please check setting"); } } private void LoadCameraDetail(AnchorPane cam, int camid, CameraConfigEnum cc){ try{ have_face[camid-1] = new AtomicBoolean(false); have_eyes[camid-1] = new AtomicBoolean(false); have_profile[camid-1] = new AtomicBoolean(false); FXMLLoader loader = new FXMLLoader(getClass().getResource("cameradetail.fxml")); AnchorPane child = loader.load(); cam.getChildren().clear(); cam.getChildren().add(child); cam.widthProperty().addListener(observable ->{ //System.out.println("Width property, cam "+camid+" width="+cam.getWidth()+" height="+cam.getHeight()); if (cam.getWidth()!=0) { if (cam.getHeight()!=0){ AnchorPane.setTopAnchor(child, 0.0); AnchorPane.setBottomAnchor(child, 0.0); AnchorPane.setLeftAnchor(child, 0.0); AnchorPane.setRightAnchor(child, 0.0); } } }); cam.heightProperty().addListener(observable -> { //System.out.println("Height property, cam "+camid+" width="+cam.getWidth()+" height="+cam.getHeight()); if (cam.getWidth()!=0) { if (cam.getHeight()!=0){ AnchorPane.setTopAnchor(child, 0.0); AnchorPane.setBottomAnchor(child, 0.0); AnchorPane.setLeftAnchor(child, 0.0); AnchorPane.setRightAnchor(child, 0.0); } } }); switch(camid){ case 1: image1 = loader.getController(); image1.setCameraConfigEnum(cc); break; case 2: image2 = loader.getController(); image2.setCameraConfigEnum(cc); break; case 3: image3 = loader.getController(); image3.setCameraConfigEnum(cc); break; case 4: image4 = loader.getController(); image4.setCameraConfigEnum(cc); break; case 5: image5 = loader.getController(); image5.setCameraConfigEnum(cc); break; } } catch (Exception e){ Logger.error("Error LoadCameraDetail: " + e.getMessage()); } } private void InsertSQL(PhotoReviewClass prc){ Sqlite sql = new Sqlite(); sql.Insert(prc); } }